package com.niiwoo.civet.trade.service.local.invest;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.fastjson.JSON;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.google.common.collect.Lists;
import com.niiwoo.activity.stage.dto.request.inviteBuddy.UserInvestEventDTO;
import com.niiwoo.activity.stage.dto.request.notice.NoviceTaskRecordRequestDTO;
import com.niiwoo.activity.stage.dto.request.prize.ActivityPrizeResetStatusReqDTO;
import com.niiwoo.activity.stage.dto.request.prize.ActivityPrizeStatusReqDTO;
import com.niiwoo.activity.stage.dto.response.prize.InvestorExtraDetailSummaryDTO;
import com.niiwoo.activity.stage.dto.response.prize.UserPrizeResponseDTO;
import com.niiwoo.activity.stage.enums.activity.NoviceTaskPrizeEnum;
import com.niiwoo.activity.stage.enums.prize.UserPrizeSuitProjectTypeEnum;
import com.niiwoo.activity.stage.service.prize.ActivityPrizeDubboService;
import com.niiwoo.asset.cardniu.dto.mq.CardNiuExternalMQConstants;
import com.niiwoo.asset.org.business.constants.OrgRabbitConstant;
import com.niiwoo.asset.org.business.dto.mq.ProjectStatusChangeDTO;
import com.niiwoo.civet.account.constant.PlateformConstant;
import com.niiwoo.civet.account.dto.common.ProjectInvestCancelOrderDTO;
import com.niiwoo.civet.account.dto.response.AccountBaseResponse;
import com.niiwoo.civet.account.service.AccountInvestDubboService;
import com.niiwoo.civet.base.constant.OrgConstant;
import com.niiwoo.civet.base.enums.BorrowProjectTypeEnum;
import com.niiwoo.civet.base.enums.DeadlineUnitEnum;
import com.niiwoo.civet.trade.constant.AmountHandleConstant;
import com.niiwoo.civet.trade.constant.MqConstant;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.InvestCurrentPeriodDTO;
import com.niiwoo.civet.trade.dto.InvestStopForEvaluationDTO;
import com.niiwoo.civet.trade.dto.ReceivedInterestDTO;
import com.niiwoo.civet.trade.dto.UserSuccessProjectCountDTO;
import com.niiwoo.civet.trade.dto.common.CreditSuccessInfoDTO;
import com.niiwoo.civet.trade.dto.common.FilterInvestConfigDTO;
import com.niiwoo.civet.trade.dto.common.InvestOrderDTO;
import com.niiwoo.civet.trade.dto.common.InvestingQueryCondtionConfigDTO;
import com.niiwoo.civet.trade.dto.request.InvestCreditDubboRequest;
import com.niiwoo.civet.trade.dto.request.InvestDubboRequest;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.IntelligenceInvestPageRequestDTO;
import com.niiwoo.civet.trade.dto.response.*;
import com.niiwoo.civet.trade.dto.response.RecommendInvestResponseDTO.ProjectInfoResponseDTO;
import com.niiwoo.civet.trade.dto.response.contract.CommissionAuthAgreementResponseDTO;
import com.niiwoo.civet.trade.dto.response.creditor.CreditorConfigDTO;
import com.niiwoo.civet.trade.dto.response.intelligenceInvest.IntelligenceInvestSwitchResponseDTO;
import com.niiwoo.civet.trade.dto.response.intelligenceInvest.NoviceIntelligenceInvestInfoResponseDTO;
import com.niiwoo.civet.trade.dto.response.invest.InvestmentRateInfoResponseDTO;
import com.niiwoo.civet.trade.dto.response.invest.ProjectStartInvestAndIncreResponseDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.enums.Intelligence.IntelligenceSwitchSourceEnum;
import com.niiwoo.civet.trade.enums.Intelligence.ProjectPackageStatusEnum;
import com.niiwoo.civet.trade.enums.creditorSale.SaleDetailStatusEnum;
import com.niiwoo.civet.trade.service.creditor.CreditorConfigDubboService;
import com.niiwoo.civet.trade.service.local.common.InvestCommonServer;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.civet.trade.service.local.contract.CreditorTransferRiskPrompt;
import com.niiwoo.civet.trade.service.local.contract.FadadaContractContext;
import com.niiwoo.civet.trade.service.local.creditor.CreditorTransferConfirmLocalService;
import com.niiwoo.civet.trade.service.local.creditor.TradableInvestOrderService;
import com.niiwoo.civet.trade.service.local.intelligenceInvest.IntelligenceInvestService;
import com.niiwoo.civet.trade.service.local.intelligenceInvest.NoviceIntelligenceInvestService;
import com.niiwoo.civet.trade.service.local.lend.ProjectLendLocalService;
import com.niiwoo.civet.trade.service.local.myloan.MyLoanLocalService;
import com.niiwoo.civet.trade.service.local.project.ProjectLocalService;
import com.niiwoo.civet.trade.utils.RepaymentUtil;
import com.niiwoo.civet.trade.utils.SysNumberUtil;
import com.niiwoo.civet.user.dto.UserPersonalDubboInfoDTO;
import com.niiwoo.civet.user.dto.request.SimpleOrgInfo;
import com.niiwoo.civet.user.dto.request.UserLetterRequestDTO;
import com.niiwoo.civet.user.dto.response.UserBasicInfoResponseDTO;
import com.niiwoo.civet.user.dto.response.UserIdentityInfoResponseDTO;
import com.niiwoo.civet.user.enums.MessageTemplateKeyEnum;
import com.niiwoo.civet.user.enums.UserForbidTypeEnum;
import com.niiwoo.civet.user.enums.UserIdentityEnum;
import com.niiwoo.civet.user.enums.UserLetterTypeEnum;
import com.niiwoo.civet.user.service.*;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.base.enums.UserTypeEnum;
import com.niiwoo.tripod.base.helper.StringHelper;
import com.niiwoo.tripod.base.plan.RepaymentPlanContext;
import com.niiwoo.tripod.base.plan.support.PeriodUnitEnum;
import com.niiwoo.tripod.base.plan.support.RepaymentPlanDTO;
import com.niiwoo.tripod.base.plan.support.RepaymentTypeEnum;
import com.niiwoo.tripod.base.utils.DateUtils;
import com.niiwoo.tripod.im.component.IMService;
import com.niiwoo.tripod.im.request.ProjectGroupCreateRequest;
import com.niiwoo.tripod.im.response.IMCommonResponse;
import com.niiwoo.tripod.lanmao.request.AsyncTransactionRequest;
import com.niiwoo.tripod.provider.dto.response.PageResponseDTO;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.provider.exception.ProjectInvestedException;
import com.niiwoo.tripod.provider.exception.ProjectInvestingException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.request.df.InvestSuccessRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;

@Service
@Slf4j
public class InvestService {

	private static final Logger logger = LoggerFactory.getLogger(InvestService.class);

	@Reference(version = "1.0.0")
	private UserForbidDubboService userForbidDubboService;
	@Reference(version = "1.0.0")
	private UserBasicInfoDubboService userBasicInfoDubboService;
	@Reference(version = "1.0.0")
	private ActivityPrizeDubboService activityPrizeDubboService;
	@Reference(version = "1.0.0")
	private UserIdentityDubboService userIdentityDubboService;
	@Reference(version = "1.0.0")
	private OrganizationDubboService organizationDubboService;
	@Reference(version = "1.0.0")
	private UserDataDubboService userDataDubboService;
	@Reference(version = "1.0.0")
	private AccountInvestDubboService accountInvestDubboService;
	@Reference(version = "1.0.0")
	private UserAuthorizationInfoDubboService userAuthorizationInfoDubboService;
	@Reference(version = "1.0.0")
	private UserLetterDubboService userLetterDubboService;
	@Autowired
	private SnowflakeIdWorker snowflakeIdWorker;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private ProjectInvestPrizeService projectInvestPrizeService;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private IMService imService;
	@Autowired
	PrivacyMasks privacyMasks;
	@Autowired
	private FadadaContractContext fadadaContractContext;
	@Autowired
	private ProjectLendLocalService projectLendLocalService;
	@Autowired
	private CreditorTransferConfirmLocalService creditorTransferConfirmLocalService;
	@Autowired
	private TradeConfigLocalService tradeConfigLocalService;
	@Autowired
	private CreditorTransferRiskPrompt creditorTransferRiskPrompt;
	@Autowired
	private TradableInvestOrderService tradableInvestOrderService;
	@Autowired
	private NoviceIntelligenceInvestService noviceIntelligenceInvestService;
	@Autowired
	private IntelligenceInvestService intelligenceInvestService;


	@Autowired
	private InvestAddInterestRateConfigMapperExt investAddInterestRateConfigMapperExt;
	@Autowired
	private InvestorCreditorMapperExt investorCreditorMapperExt;
	@Autowired
	private CreditorRateConfigMapperExt creditorRateConfigMapperExt;
	@Autowired
	private CreditorSellingMapperExt creditorSellingMapperExt;

	@Autowired
	private ProjectMapperExt projectMapperExt;
	@Autowired
	private ProjectInvestingMapperExt projectInvestingMapperExt;
	@Autowired
	private UserEvaluationRecordMapperExt userEvaluationRecordMapperExt;
	@Autowired
	private UserInvestConfigMapperExt userInvestConfigMapperExt;
	@Autowired
	private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;
	@Autowired
	private ProjectInvestorPlanMapperExt projectInvestorPlanMapperExt;
	@Autowired
	private NewbieProjectConfigMapperExt newbieProjectConfigMapper;
	@Autowired
	private ProjectInvestOrderProcessMapperExt projectInvestOrderProcessMapperExt;
	@Autowired
	private HomePageRecomConfigMapperExt homePageRecomConfigMapperExt;
	@Autowired
	private CreditorSaleDetailMapperExt creditorSaleDetailMapperExt;
	@Autowired
	private ProjectBorrowerSummaryMapperExt borrowerSummaryMapperExt;
	@Autowired
	private ProjectInvestCancelOrderMapperExt projectInvestCancelOrderMapperExt;
	@Autowired
	private ProjectContractMapperExt projectContractMapperExt;

	@Autowired
	private ProjectInvestPrizeMapperExt projectInvestPrizeMapperExt;
	@Autowired
	private OverdueTypeMapperExt overdueTypeMapperExt;
	@Autowired
	private ProjectPackageMapperExt projectPackageMapperExt;
	@Autowired
	private InvestCommonServer investCommonServer;
	@Autowired
	private ProjectLocalService projectLocalService;
	@Reference(version = "1.0.0")
	private CreditorConfigDubboService creditorConfigDubboService;
	@Autowired
	private MyLoanLocalService myLoanLocalService;


	private FastDateFormat timeFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
	private FastDateFormat dayFormat = FastDateFormat.getInstance("yyyyMMdd");
	private static final String RISK_TIPS_CONTRACT_SEQ = "invest-risk-tips-contract-seq-";

	@Transactional(isolation = READ_COMMITTED)
	public InvestOrderResponseDTO invest(InvestDubboRequest investDubboRequest, Long accountId, BigDecimal availableAmount, Date expired, Byte tender, InvestWayEnum investWayEnum, Long batchNo,
										 String platformUserNo) {
		Byte investUserType = investDubboRequest.getInvestUserType();
		String userId = investDubboRequest.getUserId();
		// 法大大授权
		investCommonServer.fadadaGrantAuthorization(investUserType, userId);
		BigDecimal investAmount = investDubboRequest.getInvestAmount();

		Long projectId = investDubboRequest.getProjectId();

		// 前置验证
		investCommonServer.investCommValidate(userId, availableAmount, investAmount, 5);
		Project projectForUpdate = projectMapperExt.selectByPrimaryKeyForUpdate(projectId);

		if(projectLocalService.isZhiNengTou(projectForUpdate.getChannelCode())) {
			throw new BizException("TRD10036");
		}
		// 超时验证
		if (projectForUpdate.getInvestEndTime().getTime() < System.currentTimeMillis()) {
			throw new BizException("TRD10029");
		}
		// 不能投资自己发起的借款标的
		if (userId.equals(projectForUpdate.getBorrowUserId())) {
			throw new BizException("TRD10016");
		}
		if (!ProjectStatusEnum.COLLECTING.getStatus().equals(projectForUpdate.getStatus())) {
			throw new ProjectInvestedException("TRD10007");
		}

		// 渠道=app 则不是新手标，无须验证新手标规则
		if (!ChannelCodeEnum.APP.getChanelCode().equalsIgnoreCase(projectForUpdate.getChannelCode())) {
			// 验证新手标
			valdataNewPersonProject(userId, projectForUpdate.getChannelCode(), investAmount);
		}

		ProjectInvesting projectInvesting = projectInvestingMapperExt.selectByPrimaryKey(projectId);
		if (null == projectInvesting || Byte.valueOf("3").equals(projectInvesting.getStatus())) {
			throw new ProjectInvestedException("TRD10007");// 标的可投额度不足
		}

		BigDecimal investedAmount = projectInvestOrderMapperExt.selectInvestedAmount(projectId);
		if (null == investedAmount) {
			investedAmount = BigDecimal.ZERO;
		}
		if (projectForUpdate.getContractAmount().compareTo(investedAmount) == 0) {
			throw new ProjectInvestedException("TRD10007");// 标的可投额度不足
		}
		BigDecimal investingAmount = projectInvestOrderMapperExt.selectInvestingAmount(projectId);
		if (null == investingAmount) {
			investingAmount = BigDecimal.ZERO;
		}
		BigDecimal investingAndInvestedAmount = investedAmount.add(investingAmount);

		// 可投余额
		BigDecimal remainingAmount = projectForUpdate.getContractAmount().subtract(investingAndInvestedAmount);
		if (remainingAmount.compareTo(investAmount) < 0) {
			throw new ProjectInvestingException("TRD10007");// 标的可投额度不足
		}


		// 投资金额不等于 剩余可投金额 ，需要检验是否整百投资
		if (remainingAmount.compareTo(investAmount) != 0) {
			ProjectStartInvestAndIncreResponseDTO responseDTO = myLoanLocalService.getProjectStartInvestAndIncrement();
			if (investAmount.compareTo(responseDTO.getStartInvestMoney()) >= 0) {
				BigDecimal incrmentValide = investAmount.subtract(responseDTO.getStartInvestMoney());
				BigDecimal[] WholeHundred = incrmentValide.divideAndRemainder(responseDTO.getIncrementAmount());
				if (BigDecimal.ZERO.compareTo(WholeHundred[1]) != 0) {
					throw new BizException("TRD10047");
				}
			} else {
				throw new BizException("TRD10046");
			}
		}

		Long investOrderId = snowflakeIdWorker.nextId();
		Long prizeId = investDubboRequest.getPrizeId();

		ProjectInvestOrder record = new ProjectInvestOrder();
		if (null != prizeId && prizeId != 0) {
			UserPrizeResponseDTO userPrizeResponseDTO = null;
			try {
				userPrizeResponseDTO = activityPrizeDubboService.getUserPrizeInfo(userId, prizeId);
			} catch (Throwable e) {
				if (e instanceof BizException) {
					throw e;
				}
				throw new BizException("TRD30003");
			}
			if (null == userPrizeResponseDTO) {
				throw new BizException("TRD30007");// 红包不存在
			}
			// 8投资加息券，9投资返现红包
			if (!Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId()) && !Byte.valueOf("9").equals(userPrizeResponseDTO.getPrizeTypeId())) {
				throw new BizException("TRD30008");// 红包类型错误
			}
			if (Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId())) {
				if (null == userPrizeResponseDTO.getInvestRate() || userPrizeResponseDTO.getInvestRate().compareTo(BigDecimal.ZERO) <= 0 || userPrizeResponseDTO.getNumberMount() == null) {
					throw new BizException("TRD30009");// 该投资加息券数据有误
				}
			}

			ProjectInvestPrize projectInvestPrize = new ProjectInvestPrize();
			projectInvestPrize.setId(investOrderId);
			projectInvestPrize.setUserId(userId);
			projectInvestPrize.setProjectId(investDubboRequest.getProjectId());
			projectInvestPrize.setInvestOrderId(investOrderId);
			projectInvestPrize.setPrizeId(prizeId);
			projectInvestPrize.setType(Byte.valueOf("1"));
			projectInvestPrize.setCreateTime(new Date());
			projectInvestPrizeService.insertSelective(projectInvestPrize);

			ActivityPrizeStatusReqDTO activityPrizeStatusReqDTO = new ActivityPrizeStatusReqDTO();
			activityPrizeStatusReqDTO.setUserId(userId);
			activityPrizeStatusReqDTO.setPrizeId(prizeId);
			activityPrizeStatusReqDTO.setProjectId(investDubboRequest.getProjectId());
			activityPrizeStatusReqDTO.setDeadLine(projectForUpdate.getDeadline());
			activityPrizeStatusReqDTO.setAmount(investAmount);
			if(projectForUpdate.getType() == 11){
				activityPrizeStatusReqDTO
						.setSuitProjectTypeEnum(UserPrizeSuitProjectTypeEnum.getTypeEnum(LoanTypeForPrizeEnum.enumOf(projectForUpdate.getLoanType()).getUserPrizeSuitProjectType()));
			}else{
				activityPrizeStatusReqDTO
						.setSuitProjectTypeEnum(UserPrizeSuitProjectTypeEnum.getTypeEnum(com.niiwoo.civet.trade.enums.ProjectTypeEnum.getEnum(projectForUpdate.getType()).getUserPrizeSuitProjectType()));
			}

			activityPrizeStatusReqDTO.setOrderNo(investOrderId);
			try {
				activityPrizeDubboService.setPrizeUsedToOccupied(activityPrizeStatusReqDTO);
			} catch (Throwable e) {
				if (e instanceof BizException) {
					throw e;
				} else {
					throw new BizException("TRD30003");
				}
			}
			record.setPrizeId(prizeId);
			record.setPrizeTypteId(userPrizeResponseDTO.getPrizeTypeId());
			record.setPrizeName(userPrizeResponseDTO.getPrizeName());
			if (Byte.valueOf("8").equals(userPrizeResponseDTO.getPrizeTypeId())) {
				BigDecimal addRate = userPrizeResponseDTO.getInvestRate().divide(new BigDecimal(100));
				record.setAddRate(addRate);
				BigDecimal addInterest = investCommonServer.getAddInterest(projectForUpdate, investDubboRequest.getInvestAmount(), userPrizeResponseDTO);
				record.setAddInterest(addInterest);
			}
		}

		// 插入投资订单
		record.setId(investOrderId);
		record.setProjectId(projectId);
		record.setProjectType(projectInvesting.getType());
		record.setBorrowUserId(projectInvesting.getBorrowUserId());
		record.setBorrowHeadImage(projectInvesting.getBorrowHeadImage());
		record.setBorrowNickName(projectInvesting.getBorrowNickName());
		record.setBorrowGender(projectInvesting.getBorrowGender());
		record.setBorrowNiiwooScore(projectInvesting.getBorrowNiiwooScore());
		record.setLoanTimes(projectInvesting.getLoanTimes());
		record.setBorrowRate(projectInvesting.getBorrowRate());
		record.setTitle(projectInvesting.getTitle());
		record.setContractAmount(projectInvesting.getContractAmount());
		record.setDeadline(projectInvesting.getDeadline());
		record.setDeadlineUnit(projectInvesting.getDeadlineUnit());
		record.setLatitude(projectInvesting.getLatitude());
		record.setLongtitude(projectInvesting.getLongitude());
		record.setLoanCity(projectInvesting.getLoanCity());
		record.setLoanArea(projectInvesting.getLoanArea());
		record.setPublishTime(projectInvesting.getPublishTime());
		record.setRepaymentType(projectInvesting.getRepaymentType());
		record.setUserId(userId);
		// 企业与用户保存的信息不一致修改
		if (UserTypeEnum.ORGANIZATION.getValue().equals(investUserType)) {
			SimpleOrgInfo orgInfo = organizationDubboService.getByOrgId(userId);
			record.setInvestorName(orgInfo.getOrganizationName());
			try {
				record.setInvestorIdCard(privacyMasks.encryptPrivacy(orgInfo.getUnifiedSocialCreditCode()));
			} catch (Exception ex) {
				log.error("投资订单身份证号加密出错{}", ex);
			}
		} else {
			UserIdentityInfoResponseDTO identityResponseDTO = userIdentityDubboService.get(userId);
			record.setInvestorName(identityResponseDTO.getName());
			try {
				record.setInvestorIdCard(privacyMasks.encryptPrivacy(identityResponseDTO.getIdCard()));
			} catch (Exception ex) {
				log.error("投资订单身份证号加密出错{}", ex);
			}
		}

		record.setAccountId(accountId);
		// 增加投资人平台用户编码
		record.setPlatformUserNo(platformUserNo);
		record.setAmount(investDubboRequest.getInvestAmount());
		List<RepaymentPlanDTO> repaymentPlan = RepaymentPlanContext.getRepaymentPlan(RepaymentTypeEnum.enumOf(projectForUpdate.getRepaymentType()), projectForUpdate.getType(),
				investDubboRequest.getInvestAmount(), projectForUpdate.getBorrowRate(), projectForUpdate.getDeadline(), PeriodUnitEnum.enumOf(projectForUpdate.getDeadlineUnit()), new Date());
		BigDecimal interest = BigDecimal.ZERO;
		for (RepaymentPlanDTO repaymentPlanDTO : repaymentPlan) {
			interest = interest.add(repaymentPlanDTO.getInterest());
		}
		log.info("investOrderId:{},interest:{}", investOrderId, interest);

		if (projectForUpdate.getActivityRaiseRate() != null) {
			List<RepaymentPlanDTO> activityRaiseRepaymentPlan = RepaymentPlanContext.getRepaymentPlan(RepaymentTypeEnum.enumOf(projectForUpdate.getRepaymentType()), projectForUpdate.getType(),
					investDubboRequest.getInvestAmount(), projectForUpdate.getActivityRaiseRate(), projectForUpdate.getDeadline(), PeriodUnitEnum.enumOf(projectForUpdate.getDeadlineUnit()),
					new Date());
			for (RepaymentPlanDTO repaymentPlanDTO : activityRaiseRepaymentPlan) {
				interest = interest.add(repaymentPlanDTO.getInterest());
			}
			log.info("investOrderId:{},activityRaiseRate after interest:{}", investOrderId, interest);
		}

		record.setInterest(interest);
		record.setStatus(ProjectInvestOrderStatusEnum.PENDING_PAYMENT.getValue());
		record.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.DEALING.getValue());
		record.setType(investWayEnum.getValue());
		record.setExpireTime(expired);
		record.setCreateTime(new Date());
		// 已作废字段，不使用
		record.setOverdueType(OverdueTypeEnum._31D_RULE.getCode());
		record.setBatchNo(batchNo);
		record.setIsFull(remainingAmount.compareTo(investDubboRequest.getInvestAmount()) == 0 ? Byte.valueOf("1") : Byte.valueOf("0"));
		record.setInvestorType(investUserType);// 投资人类型
		projectInvestOrderMapperExt.insertSelective(record);

		// 更改标的已投金额
		BigDecimal newInvestedAmount = projectForUpdate.getInvestedAmount().add(record.getAmount());
		Project projectUpdater = new Project();
		projectUpdater.setBorrowUserId(projectForUpdate.getBorrowUserId());
		projectUpdater.setProjectId(projectId);
		projectUpdater.setInvestedAmount(newInvestedAmount);
		projectMapperExt.updateByPrimaryKeySelective(projectUpdater);
		// 更改投资中标的已投金额
		ProjectInvesting projectInvestingUpdater = new ProjectInvesting();
		projectInvestingUpdater.setProjectId(projectId);
		projectInvestingUpdater.setInvestedAmount(newInvestedAmount);
		if (projectForUpdate.getContractAmount().compareTo(newInvestedAmount) == 0) {
			projectInvestingUpdater.setStatus(Byte.valueOf("2"));// 已抢光
		}
		projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvestingUpdater);
		InvestOrderResponseDTO result = new InvestOrderResponseDTO();
		result.setInvestOrderId(investOrderId);
		result.setExpired(expired);
		result.setTender(tender);
		result.setProjectType(projectForUpdate.getType());
		result.setProjectTitle(projectForUpdate.getTitle());
		result.setDeadline(projectForUpdate.getDeadline());
		result.setDeadlineUnit(projectForUpdate.getDeadlineUnit());
		result.setBorrowUserId(projectForUpdate.getBorrowUserId());
		// 风险提示书签署法大大合同
		rabbitTemplate.convertAndSend(RabbitConstant.Exchange.SIGN_RISK_TIPS_AGREEMENT, RabbitConstant.RoutingKey.SIGN_RISK_TIPS_AGREEMENT, investOrderId);
		return result;
	}

	/**
	 * 如果不属于新手标，则不处理 属于新手标则验证新手期限以及新手可投资金额
	 *
	 * @param userId
	 * @param channelCode
	 * @param investAmount
	 * @return
	 */
	private boolean valdataNewPersonProject(String userId, String channelCode, BigDecimal investAmount) {
		NoviceInvestInfoResponseDTO infoResponse = getNoviceInvestInfos(userId, channelCode);
		Byte newProjectYes = Byte.valueOf("1");
		Byte newPersonYes = Byte.valueOf("1");
		// 不属于新手标则不校验
		if (!newProjectYes.equals(infoResponse.getIsNewProject())) {
			return false;
		}
		BigDecimal surplusAmount = Objects.isNull(infoResponse.getNoviceSurplusInvestMoney()) ? BigDecimal.ZERO : infoResponse.getNoviceSurplusInvestMoney();
		if (!newPersonYes.equals(infoResponse.getIsNovice())) {
			throw new BizException("TRD10030");// 您已过新手期，无法投资新手标。
		}
		if (investAmount.compareTo(surplusAmount) > 0) {
			throw new BizException("TRD10031");// 您的新手可投资金额不足，请重新输入投资金额。
		}
		return true;
	}

	@Transactional(isolation = READ_COMMITTED)
	public InvestCreditResponseDTO investCreditor(InvestCreditDubboRequest investDubboRequest, AccountBaseResponse accountBase, Date expired) {

		// 法大大授权
		userAuthorizationInfoDubboService.fadadaAuth(investDubboRequest.getUserId());
		if (investDubboRequest.getInvestAmount().compareTo(BigDecimal.ONE) < 0) {
			throw new BizException("TRD10005");
		}

		String userId = investDubboRequest.getUserId();
		BigDecimal accountAvailableAmount = accountBase.getAvailableAmount();
		BigDecimal investAmount = investDubboRequest.getInvestAmount();
		// 前置验证 是否行为禁止 ，投资中的订单是否超过5个，余额是否充足
		preCondition(userId, accountAvailableAmount, investAmount);
		// 悲观锁 防止销售的债权被取消
		CreditorSaleDetail creditorSaleDetail = creditorSaleDetailMapperExt.selectByPrimaryKeyAndUserIdForUpdate(investDubboRequest.getCreditorSaleDetailId(),
				investDubboRequest.getCreditorSaleUserId());

		if (Objects.isNull(creditorSaleDetail)) {
			throw new BizException("TRD1000017");// 当前债权状态已变更，请刷新列表
		}
		Project project = projectMapperExt.selectByPrimaryKey(creditorSaleDetail.getProjectId());
		// 投资人不能使借款人
		if (project.getBorrowUserId().equals(userId)) {
			throw new BizException("TRD10016");
		}
		if (!SaleDetailStatusEnum.SELLING.getValue().equals(creditorSaleDetail.getStatus())) {
			throw new BizException("TRD1000017");// 当前债权状态已变更，请刷新列表
		}
		// 已过期
		if (creditorSaleDetail.getExpireTime().getTime() < System.currentTimeMillis()) {
			throw new BizException("TRD1000017");// 当前债权状态已变更，请刷新列表
		}

		CreditorSaleDetail detail = new CreditorSaleDetail();
		BeanUtils.copyProperties(creditorSaleDetail, detail);
		// 计算出让期间利息
		tradableInvestOrderService.reCalcAmount(detail);
		BigDecimal saleAmount = creditorSaleDetail.getSaleAmount();
		BigDecimal sellingInterest = Objects.isNull(detail.getSellingInterest()) ? BigDecimal.ZERO : detail.getSellingInterest();
		saleAmount = saleAmount.add(sellingInterest);
		if (investAmount.compareTo(saleAmount) != 0) {
			logger.warn("投资金额与实际出让金额不一致，债权出让人{}，债权出让ID{}，投资金额{}，出让金额{}，出让期间利息{}", investDubboRequest.getCreditorSaleUserId(), investDubboRequest.getCreditorSaleDetailId(),
					investDubboRequest.getInvestAmount(), creditorSaleDetail.getSaleAmount(), sellingInterest);
			throw new BizException("TRD1000018");// 投资金额不正确 前端传递的投资金额与实际投资金额不一致
		}
		// 原始投资订单
		ProjectInvestOrder investOrder = projectInvestOrderMapperExt.selectByPrimaryKey(creditorSaleDetail.getInvestOrderId());
		// 生成债权投资订单
		ProjectInvestOrder creditOrder = generateCreditOrder(investDubboRequest, creditorSaleDetail, investOrder, detail, accountBase, expired);
		// 投资订单生成
		projectInvestOrderMapperExt.insertSelective(creditOrder);
		// 债权出让状态修改
		creditorSaleDetail.setStatus(SaleDetailStatusEnum.PRE_CREDIT_ASSIGNMENT_HANDING.getValue());
		creditorSaleDetail.setSellingExpenseAmount(detail.getSellingExpenseAmount());
		creditorSaleDetail.setSellingInterest(sellingInterest);// 出让期间利息
		creditorSaleDetail.setArrivalAmount(detail.getArrivalAmount());// 实际出让金额
		creditorSaleDetail.setOffAmount(detail.getOffAmount());// 实际让利金额
		// 已经锁表 可以直接修改
		creditorSaleDetailMapperExt.updateByPrimaryKeyAndUserIdSelective(creditorSaleDetail);

		// 销售中的
		CreditorSelling creditorSelling = new CreditorSelling();
		creditorSelling.setCreditorSaleDetailId(creditorSaleDetail.getId());
		creditorSelling.setStatus(Byte.valueOf("2"));
		creditorSellingMapperExt.updateByPrimaryKeySelective(creditorSelling);

		InvestCreditResponseDTO result = new InvestCreditResponseDTO();
		result.setInvestOrderId(creditOrder.getId());
		result.setExpired(expired);
		result.setTender(accountBase.getTender());
		result.setShare(creditorSaleDetail.getShare());
		result.setCreditsaleRequestNo(creditorSaleDetail.getRequestNo());

		result.setProjectType(BorrowProjectTypeEnum.TRANSFER_OF_CREDIT.getValue());// 债权标
		StringBuilder sb = new StringBuilder();
		sb.append("投资债权标-");
		Integer month = creditorSaleDetail.getRemainingDeadlineMonth();
		Integer day = creditorSaleDetail.getRemainingDeadlineDay();
		if (Objects.nonNull(month) && month != 0) {
			sb.append(month).append("个月");
		}
		if (Objects.nonNull(day) && day != 0) {
			sb.append(day).append("天");
		}
		sb.append("(").append(project.getBorrowRate().multiply(BigDecimal.valueOf(100)).stripTrailingZeros().toPlainString()).append("%").append(")");
		result.setProjectTitle(sb.toString());// 标题栏定制
		result.setDeadline(project.getDeadline());
		result.setDeadlineUnit(project.getDeadlineUnit());
		result.setBorrowUserId(creditorSaleDetail.getUserId());// 记账 债权出让人
		result.setProjectId(creditorSaleDetail.getProjectId());
		result.setCreditorSaleDetailId(creditorSaleDetail.getId());// 记账 债权出让ID
		// 风险提示书签署法大大合同
		rabbitTemplate.convertAndSend(RabbitConstant.Exchange.SIGN_RISK_TIPS_AGREEMENT, RabbitConstant.RoutingKey.SIGN_RISK_TIPS_AGREEMENT, creditOrder.getId());
		return result;
	}

	/**
	 * 生成新的债权投资订单
	 *
	 * @param investDubboRequest
	 *            债权投资请求参数
	 * @param creditorSaleDetail
	 *            债权出让参数
	 * @param investOrder
	 *            出让人原
	 * @param detail
	 *            每日计息计算结果
	 * @param accountBase
	 *            账户信息
	 * @param expired
	 *            超时时间
	 * @return
	 */
	private ProjectInvestOrder generateCreditOrder(InvestCreditDubboRequest investDubboRequest, CreditorSaleDetail creditorSaleDetail, ProjectInvestOrder investOrder, CreditorSaleDetail detail,
												   AccountBaseResponse accountBase, Date expired) {
		ProjectInvestOrder creditOrder = new ProjectInvestOrder();
		InvestWayEnum investWay = InvestWayEnum.MANUAL_GATEWAY;
		if (Byte.valueOf("1").equals(accountBase.getTender())) {
			investWay = InvestWayEnum.MANUAL_DIRECT;
		}
		creditOrder.setId(snowflakeIdWorker.nextId());
		// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓沿用旧标的数据↓↓↓↓↓↓↓↓
		creditOrder.setProjectId(investOrder.getProjectId());
		creditOrder.setProjectType(investOrder.getProjectType());
		creditOrder.setBorrowUserId(investOrder.getBorrowUserId());
		creditOrder.setBorrowHeadImage(investOrder.getBorrowHeadImage());
		creditOrder.setBorrowNickName(investOrder.getBorrowNickName());
		creditOrder.setBorrowGender(investOrder.getBorrowGender());
		creditOrder.setBorrowNiiwooScore(investOrder.getBorrowNiiwooScore());
		creditOrder.setLoanTimes(investOrder.getLoanTimes());
		creditOrder.setTitle(investOrder.getTitle());
		creditOrder.setContractAmount(investOrder.getContractAmount());
		creditOrder.setBorrowRate(investOrder.getBorrowRate());
		creditOrder.setDeadline(investOrder.getDeadline());
		creditOrder.setDeadlineUnit(investOrder.getDeadlineUnit());
		creditOrder.setLongtitude(investOrder.getLongtitude());
		creditOrder.setLatitude(investOrder.getLatitude());
		creditOrder.setLoanCity(investOrder.getLoanCity());
		creditOrder.setLoanArea(investOrder.getLoanArea());
		creditOrder.setPublishTime(investOrder.getPublishTime());
		creditOrder.setRepaymentType(investOrder.getRepaymentType());
		creditOrder.setFullSuccessTime(investOrder.getFullSuccessTime());
		creditOrder.setOverdueType(investOrder.getOverdueType());
		// ↑↑↑↑↑↑↑↑↑以上沿用旧标的数据↑↑↑↑↑↑↑↑↑↑↑
		// ↓↓↓以下投资人信息改为当前债权认购人信息↓↓↓↓↓↓↓↓↓
		creditOrder.setUserId(investDubboRequest.getUserId());
		creditOrder.setPlatformUserNo(accountBase.getAccountNo());
		creditOrder.setInvestType(Byte.valueOf("2"));
		UserIdentityInfoResponseDTO identityResponseDTO = userIdentityDubboService.get(investDubboRequest.getUserId());
		creditOrder.setInvestorName(identityResponseDTO.getName());
		try {
			creditOrder.setInvestorIdCard(privacyMasks.encryptPrivacy(identityResponseDTO.getIdCard()));
		} catch (Exception ex) {
			log.error("投资订单身份证号加密出错{}", ex);
			throw new BizException("TRD1000020");// 身份证异常
		}
		creditOrder.setAccountId(accountBase.getAccountId());
		creditOrder.setStatus(ProjectInvestOrderStatusEnum.PENDING_PAYMENT.getValue());
		creditOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.DEALING.getValue());
		creditOrder.setAmount(investDubboRequest.getInvestAmount());
		List<ProjectInvestorPlan> plans = projectInvestorPlanMapperExt.listProjectInvestorPlanNotReceived(creditorSaleDetail.getInvestOrderId(), creditorSaleDetail.getProjectId());
		// 受让人预期利息 =剩余利息 - （转让金额 - 债权份额 + 出让期间利息）
		BigDecimal creditInterest = creditorSaleDetail.getSaleAmount().subtract(creditorSaleDetail.getShare()).add(detail.getSellingInterest());
		BigDecimal totalInterest = BigDecimal.ZERO;
		for (ProjectInvestorPlan projectInvestorPlan : plans) {
			totalInterest = totalInterest.add(projectInvestorPlan.getInterest());
		}
		creditOrder.setInterest(totalInterest.subtract(creditInterest));
		creditOrder.setType(investWay.getValue());
		creditOrder.setClosedReason(null);
		creditOrder.setPrizeId(null);
		creditOrder.setPrizeTypteId(null);
		creditOrder.setPrizeName(null);
		creditOrder.setAddRate(null);
		creditOrder.setAddInterest(null);
		creditOrder.setBatchNo(null);
		creditOrder.setExpireTime(expired);
		creditOrder.setCreditorSaleDetailId(creditorSaleDetail.getId());
		creditOrder.setCreditorSaleUserId(investDubboRequest.getCreditorSaleUserId());
		creditOrder.setShare(creditorSaleDetail.getShare());
		creditOrder.setIsFull(Byte.valueOf("0"));
		creditOrder.setCreateTime(new Date());
		creditOrder.setUpdateTime(new Date());
		return creditOrder;
	}

	private void preCondition(String userId, BigDecimal amount, BigDecimal investAmount) {
		// 判断行为禁止
		userForbidDubboService.validUserForbidInfo(userId, UserForbidTypeEnum.INVESTOR);
		BigDecimal accountAvailableAmount = amount;
		if (accountAvailableAmount.compareTo(investAmount) < 0) {
			throw new BizException("TRD10003");// 用户余额不足，需引导用户充值
		}
		// 判断是否有5个待支付的投资订单
		int investingCount = projectInvestOrderMapperExt.selectInvestingCountByUserId(userId);
		if (investingCount >= 5) {
			throw new BizException("TRD30002");
		}

	}

	public void validateRiskEvaluating(String userId) {
		// 判断用户是否完成风险测评
		if (!HintManagerHolder.isMasterRouteOnly()) {
			HintManagerHolder.clear();
			HintManager hintManager = HintManager.getInstance();
			hintManager.setMasterRouteOnly();
		}
		UserEvaluationRecord userEvaluationRecord = userEvaluationRecordMapperExt.selectByUserIdLimitOne(userId);
		if (null == userEvaluationRecord) {
			throw new BizException("TRD10002");// 投资前请完成风险测评
		}
	}

	/**
	 * 投资订单生成
	 *
	 * @param investDubboRequest
	 * @param accountId
	 * @param availableAmount
	 * @param expired
	 * @param tender
	 * @return
	 */
	@Transactional(isolation = READ_COMMITTED)
	public InvestOrderResponseDTO invest(InvestDubboRequest investDubboRequest, Long accountId, BigDecimal availableAmount, Date expired, Byte tender, String platformUserNo) {
		InvestWayEnum investWay = InvestWayEnum.MANUAL_GATEWAY;
		if (Byte.valueOf("1").equals(tender)) {
			investWay = InvestWayEnum.MANUAL_DIRECT;
		}
		return this.invest(investDubboRequest, accountId, availableAmount, expired, tender, investWay, null, platformUserNo);
	}

	/**
	 * 投资订单完成
	 *
	 * @param investOrderId
	 * @param isSuccess
	 * @param isAbsolutely
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void investCompleteHandle(Long investOrderId, boolean isSuccess, boolean isAbsolutely, Date queryTime) {
		ProjectInvestOrder record = projectInvestOrderMapperExt.selectByPrimaryKeyForUpdate(investOrderId);
		Long projectId = record.getProjectId();
		if (!Byte.valueOf("0").equals(record.getStatus())) {
			return;
		}
		ProjectInvestOrder updater = new ProjectInvestOrder();
		updater.setId(investOrderId);
		if (isSuccess) {
			updater.setStatus(Byte.valueOf("1"));
			updater.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.INVESTING.getValue());
			projectInvestOrderMapperExt.updateByPrimaryKeySelective(updater);

			Project project = projectMapperExt.selectByPrimaryKey(projectId);
			if(enableAddGroup(project.getType(), record.getInvestType(), record.getInvestorType(), project.getOrgId())) {
				// 插入建项目群任务
				ProjectInvestOrderProcess projectInvestOrderProcess = new ProjectInvestOrderProcess();
				projectInvestOrderProcess.setInvestOrderId(investOrderId);
				projectInvestOrderProcess.setProjectId(record.getProjectId());
				projectInvestOrderProcess.setProjectType(record.getProjectType());
				projectInvestOrderProcess.setBorrowUserId(record.getBorrowUserId());
				projectInvestOrderProcess.setTitle(record.getTitle());
				projectInvestOrderProcess.setUserId(record.getUserId());
				projectInvestOrderProcess.setAddGroupStatus(Byte.valueOf("1"));
				projectInvestOrderProcess.setCreateTime(new Date());
				projectInvestOrderProcess.setUpdateTime(new Date());
				projectInvestOrderProcessMapperExt.insertSelective(projectInvestOrderProcess);
			}

			// 如果有使用红包则投资红包使用状态变更为已占用
			if (null != record.getPrizeId()) {
				ProjectInvestPrize projectInvestPrize = new ProjectInvestPrize();
				projectInvestPrize.setId(record.getId());
				projectInvestPrize.setStatus(Byte.valueOf("1"));
				projectInvestPrizeMapperExt.updateByPrimaryKeySelective(projectInvestPrize);
			}
			// 债权的需要修改债权销售中的状态
			if (Byte.valueOf("2").equals(record.getInvestType())) {
				CreditorSelling creditorSelling = new CreditorSelling();
				creditorSelling.setCreditorSaleDetailId(record.getCreditorSaleDetailId());
				creditorSelling.setStatus(Byte.valueOf("3"));
				creditorSelling.setUpdateTime(new Date());
				creditorSellingMapperExt.updateByPrimaryKeySelective(creditorSelling);
			}

			// 投资成功后置处理
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INVEST_POST_PROCESS, RabbitConstant.RoutingKey.INVEST_POST_PROCESS, investOrderId);
		} else if (isAbsolutely || queryTime.after(record.getExpireTime())) {// 投资订单关闭
			updater.setStatus(Byte.valueOf("2"));
			updater.setClosedReason(InvestOrderCloseReasonEnum.PAY_TIMEOUT.getValue());
			updater.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue());
			projectInvestOrderMapperExt.updateByPrimaryKeySelective(updater);
			// 失败的时候债权继续出售↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
			if (Byte.valueOf("2").equals(record.getInvestType())) {
				logger.warn("债权投资超时订单未支付，投资人{}，债权ID{}，债权出让人{}", updater.getUserId(), updater.getCreditorSaleDetailId(), updater.getCreditorSaleUserId());
				CreditorSaleDetail creditorSaleDetail = new CreditorSaleDetail();
				creditorSaleDetail.setId(record.getCreditorSaleDetailId());
				creditorSaleDetail.setUserId(record.getCreditorSaleUserId());
				creditorSaleDetail.setStatus(SaleDetailStatusEnum.SELLING.getValue());
				creditorSaleDetailMapperExt.updateByPrimaryKeyAndUserIdSelective(creditorSaleDetail);
				// 债权的需要修改债权销售中的状态
				CreditorSelling creditorSelling = new CreditorSelling();
				creditorSelling.setCreditorSaleDetailId(record.getCreditorSaleDetailId());
				creditorSelling.setStatus(Byte.valueOf("1"));
				creditorSelling.setUpdateTime(new Date());
				creditorSellingMapperExt.updateByPrimaryKeySelective(creditorSelling);
				return;
			}
			// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑债权状态修改↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
			// 更改标的已投金额
			Project projectForUpdate = projectMapperExt.selectByPrimaryKeyForUpdate(projectId);
			Project projectUpdater = new Project();
			projectUpdater.setBorrowUserId(projectForUpdate.getBorrowUserId());
			projectUpdater.setProjectId(projectId);
			projectUpdater.setInvestedAmount(projectForUpdate.getInvestedAmount().subtract(record.getAmount()));
			projectMapperExt.updateByPrimaryKeySelective(projectUpdater);
			// 更改投资中标的已投金额
			ProjectInvesting projectInvestingUpdater = new ProjectInvesting();
			projectInvestingUpdater.setProjectId(projectId);
			projectInvestingUpdater.setInvestedAmount(projectForUpdate.getInvestedAmount().subtract(record.getAmount()));
			projectInvestingUpdater.setStatus(Byte.valueOf("1"));// 可投资
			projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvestingUpdater);
			// 如果使用了红包，在事务结束后退回红包
			if (null != record.getPrizeId()) {
				this.resetPrizeToReturned(investOrderId, record.getPrizeId(), record.getUserId(), record.getInvestType(), record.getId());
			}
		}
	}

	public ProjectInvesting queryInvestingProjectInfo(Long projectId) {
		if (!HintManagerHolder.isMasterRouteOnly()) {
			HintManager.getInstance().setMasterRouteOnly();
		}
		return projectInvestingMapperExt.selectByPrimaryKey(projectId);
	}

	public List<InvestingQueryCondtionConfigDTO> queryInvestingQueryCondtionConfig(BorrowerTypeEnum borrowerType) {
		List<InvestingQueryCondtionConfigDTO> configList = new ArrayList<>();

		/*
		 * List<InvestStdRelationConfig> relationConfigList = null; if
		 * (BorrowerTypeEnum.PERSONAL.equals(borrowerType)) { // 个人 relationConfigList =
		 * investStdRelationConfigMapperExt.selectByUserType((byte) 1); } else if
		 * (BorrowerTypeEnum.COMPANY.equals(borrowerType)) { // 企业 relationConfigList =
		 * investStdRelationConfigMapperExt.selectByUserType((byte) 2); }
		 *
		 * for (InvestStdRelationConfig cfg : relationConfigList) { if
		 * (BorrowerTypeEnum.PERSONAL.equals(borrowerType)) { // 个人 if
		 * (cfg.getDeadlineId() == null || cfg.getRateId() == null ||
		 * cfg.getRapaymentTypeId() == null) { continue; } } else if
		 * (BorrowerTypeEnum.COMPANY.equals(borrowerType)) { // 企业 if (cfg.getRateId()
		 * == null || cfg.getRapaymentTypeId() == null) { continue; } }
		 *
		 * InvestingQueryCondtionConfigDTO configDTO = new
		 * InvestingQueryCondtionConfigDTO();
		 *
		 * configDTO.setConfigId(cfg.getId());
		 * configDTO.setBorrowerType(borrowerType.getType());
		 *
		 * // 期限配置 if (cfg.getDeadlineId() != null) { InvestStdConfig config =
		 * investStdConfigMapperExt.selectByPrimaryKey(cfg.getDeadlineId()); if (config
		 * != null) { configDTO.setPeriodTitle(config.getTitle());
		 * configDTO.setPeriodType(config.getTermUnit());
		 * configDTO.setPeriodBegin(config.getMinval().intValue());
		 * configDTO.setPeriodBeginQueryType(config.getMinvalCalSymbol());
		 * configDTO.setPeriodEnd(config.getMaxval().intValue());
		 * configDTO.setPeriodEndQueryType(config.getMaxvalCalSymbol());
		 * configDTO.setPeriodSortValue(config.getSeq()); } }
		 *
		 * // 利率配置 if (cfg.getRateId() != null) { InvestStdConfig config =
		 * investStdConfigMapperExt.selectByPrimaryKey(cfg.getRateId()); if (config !=
		 * null) { configDTO.setRateTitle(config.getTitle());
		 * configDTO.setRateBegin(config.getMinval().divide(new BigDecimal("100")));
		 * configDTO.setRateBeginQueryType(config.getMinvalCalSymbol());
		 * configDTO.setRateEnd(config.getMaxval().divide(new BigDecimal("100")));
		 * configDTO.setRateEndQueryType(config.getMaxvalCalSymbol());
		 * configDTO.setRateSortValue(config.getSeq()); } }
		 *
		 * // 收益方式配置 if (cfg.getRapaymentTypeId() != null) { InvestStdConfig config =
		 * investStdConfigMapperExt.selectByPrimaryKey(cfg.getRapaymentTypeId()); if
		 * (config != null) { configDTO.setRepaymentTypeTitle(config.getTitle());
		 * configDTO.setRepaymentTypeValue(config.getMinval().byteValue());
		 * configDTO.setRepaymentTypeSortValue(config.getSeq()); } }
		 *
		 * configList.add(configDTO); }
		 */

		return configList;
	}

	/**
	 * 获取我的投资订单列表
	 *
	 * @param userId
	 * @param orderStatus
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public List<InvestOrderDTO> getMyInvestOrderPage(String userId, Integer orderStatus, Integer pageNumber, Integer pageSize) {
		int offset = (pageNumber - 1) * pageSize;
		List<ProjectInvestOrder> projectInvestOrders = projectInvestOrderMapperExt.selectMyInvestOrderList(userId, orderStatus, offset, pageSize);
		// 回款中和回款预期的投资订单集合
		List<Long> backingInvestOrderList = new ArrayList<>();
		// 已结清投资订单集合
		List<Long> backedInvestOrderList = new ArrayList<>();
		// 标的订单集合
		Set<Long> projectIdSet = new HashSet<>();
		// 借款用户结合
		Set<String> borrowUserIdSet = new HashSet<>();
		// 已结清的使用投资加息券的红包id集合
		List<Long> prizeIdList = new ArrayList<>();
		for (ProjectInvestOrder projectInvestOrder : projectInvestOrders) {
			// 回款中的投资订单需要获取当前期数
			if (ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue().equals(projectInvestOrder.getEvolutionStatus())
					|| ProjectInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
				backingInvestOrderList.add(projectInvestOrder.getId());
			}
			// 已结清投资订单集合
			if (ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
				backedInvestOrderList.add(projectInvestOrder.getId());
			}
			// 已结清并且使用投资加息券,需要从活动获取已加息（实际到账）
			if (ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue().equals(projectInvestOrder.getEvolutionStatus()) && null != projectInvestOrder.getPrizeId()
					&& Byte.valueOf("8").equals(projectInvestOrder.getPrizeTypteId())) {
				prizeIdList.add(projectInvestOrder.getPrizeId());
			}
			projectIdSet.add(projectInvestOrder.getProjectId());
			borrowUserIdSet.add(projectInvestOrder.getBorrowUserId());
		}
		// 获取加息
		Map<Long, BigDecimal> prizeIdAddInterestMap = new HashMap<>();
		if (!prizeIdList.isEmpty()) {
			List<InvestorExtraDetailSummaryDTO> investorExtralDtlSummary = null;
			try {
				investorExtralDtlSummary = activityPrizeDubboService.getInvestorExtralDtlSummary(prizeIdList);
			} catch (Exception e) {
				log.error("无法获取加息券信息，prizeIdList=" + JSON.toJSONString(prizeIdList));
			}
			if (null != investorExtralDtlSummary) {
				for (InvestorExtraDetailSummaryDTO investorExtraDetailSummaryDTO : investorExtralDtlSummary) {
					prizeIdAddInterestMap.put(investorExtraDetailSummaryDTO.getPrizeId(), investorExtraDetailSummaryDTO.getReceivedExtInterest());
					logger.info("投资加息券，prizeId={},receivedExtInterest={}", investorExtraDetailSummaryDTO.getPrizeId(), investorExtraDetailSummaryDTO.getReceivedExtInterest());
				}
			}
		}
		// 实际已回利息
		Map<Long, BigDecimal> investOrderIdReceivedInterestMap = new HashMap<>();
		if (!backedInvestOrderList.isEmpty()) {
			List<ReceivedInterestDTO> receivedInterestDTOS = projectInvestorPlanMapperExt.listReceivedInterestGroupByInvestOrderId(backedInvestOrderList);
			if (null != receivedInterestDTOS) {
				for (ReceivedInterestDTO receivedInterestDTO : receivedInterestDTOS) {
					investOrderIdReceivedInterestMap.put(receivedInterestDTO.getInvestOrderId(), receivedInterestDTO.getReceivedInterest());
				}
			}
		}

		// 获取借款用户发起借款的次数（已筹款成功）
		Map<String, Integer> userIdSuccessCountMap = new HashMap<>();
		if (!borrowUserIdSet.isEmpty()) {
			List<UserSuccessProjectCountDTO> userSuccessProjectCountDTOS = projectMapperExt
					.selectUserSuccessProjectCountDTOList(Arrays.asList(borrowUserIdSet.toArray(new String[borrowUserIdSet.size()])));
			for (UserSuccessProjectCountDTO userSuccessProjectCountDTO : userSuccessProjectCountDTOS) {
				userIdSuccessCountMap.put(userSuccessProjectCountDTO.getUserId(), userSuccessProjectCountDTO.getSuccessCount());
			}
		}
		// 获取投资订单的期数
		Map<Long, Integer> investOrderIdMapCurrentPeriod = new HashMap<>();
		if (!backingInvestOrderList.isEmpty()) {
			// 逾期收款
			List<InvestCurrentPeriodDTO> investCurrentPeriodDTOS = projectInvestorPlanMapperExt.listInvestOrderCurrentPeriod(backingInvestOrderList);
			for (InvestCurrentPeriodDTO investCurrentPeriodDTO : investCurrentPeriodDTOS) {
				if (null != investCurrentPeriodDTO)
					investOrderIdMapCurrentPeriod.put(investCurrentPeriodDTO.getInvestOrderId(), investCurrentPeriodDTO.getCurrentPeriod());
			}
		}

		// 获取借款居间合同
		Map<Long, String> projectIdMapContranctUrl = new HashMap<>();
		if (!projectIdSet.isEmpty()) {
			List<ProjectContract> projectContracts = projectContractMapperExt.queryFastLoanContractByProjectIdSet(projectIdSet);
			for (ProjectContract projectContract : projectContracts) {
				projectIdMapContranctUrl.put(projectContract.getProjectId(), projectContract.getViewUrl());
			}
		}

		// 获取风险提示书合同
		Map<Long, ProjectContract> orderRiskTipsMap = new HashMap<>();
		if (!projectIdSet.isEmpty()) {
			List<ProjectContract> projectContracts = projectContractMapperExt.queryInvestRiskTipsContactByProjectIdSet(userId, projectIdSet);
			for (ProjectContract projectContract : projectContracts) {
				orderRiskTipsMap.put(projectContract.getOrderId(), projectContract);
			}
		}

		// 获取标的信息
		Map<Long, Project> projectMap = new HashMap<>();
		if (!projectIdSet.isEmpty()) {
			List<Project> projectList = projectMapperExt.selectListByProjectIdSet(Arrays.asList(projectIdSet.toArray(new Long[projectIdSet.size()])));
			for (Project project : projectList) {
				projectMap.put(project.getProjectId(), project);
			}
		}

		List<InvestOrderDTO> resultList = new ArrayList<>();
		for (ProjectInvestOrder projectInvestOrder : projectInvestOrders) {
			InvestOrderDTO dto = new InvestOrderDTO();
			dto.setBorrowerType(projectMap.get(projectInvestOrder.getProjectId()).getBorrowerType());
			dto.setOrgId(projectMap.get(projectInvestOrder.getProjectId()).getOrgId());
			dto.setLoanType(projectMap.get(projectInvestOrder.getProjectId()).getLoanType());
			dto.setInvestOrderId(projectInvestOrder.getId());
			dto.setProjectId(projectInvestOrder.getProjectId());
			dto.setProjectType(projectInvestOrder.getProjectType());
			dto.setBorrowerUserId(projectInvestOrder.getBorrowUserId());
			dto.setBorrowerGender(projectInvestOrder.getBorrowGender());
			dto.setBorrowerHeadImage(projectInvestOrder.getBorrowHeadImage());
			dto.setBorrowerNickName(projectInvestOrder.getBorrowNickName());
			dto.setBorrowerNiiwooScore(projectInvestOrder.getBorrowNiiwooScore());
			dto.setLoanCity(projectInvestOrder.getLoanCity());
			dto.setLoanArea(projectInvestOrder.getLoanArea());
			dto.setFullSuccessDate(null == projectMap.get(projectInvestOrder.getProjectId()) ? null : projectMap.get(projectInvestOrder.getProjectId()).getFullSuccessTime());
			dto.setTitle(projectInvestOrder.getTitle());
			dto.setBorrowRate(projectInvestOrder.getBorrowRate());
			dto.setDeadLine(projectInvestOrder.getDeadline());
			dto.setDeadLineUtit(projectInvestOrder.getDeadlineUnit());
			dto.setContractAmount(projectInvestOrder.getContractAmount());
			dto.setOrderStatus(projectInvestOrder.getEvolutionStatus());
			if (ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {// 已流标关闭的投资订单已投金额显示为0
				dto.setInvestedAmount(BigDecimal.ZERO.setScale(2));
			} else {
				dto.setInvestedAmount(projectInvestOrder.getAmount());
			}
			if (ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {// 已流标关闭的投资订单实际收益显示为0
				dto.setInterest(BigDecimal.ZERO.setScale(2));
				dto.setAddInterest(BigDecimal.ZERO.setScale(2));
			} else if (ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {// 已结清的投资订单实际收益=实际已收利息+实际已收加息
				BigDecimal interest = BigDecimal.ZERO.setScale(2);
				if (null != investOrderIdReceivedInterestMap.get(projectInvestOrder.getId())) {
					interest = interest.add(investOrderIdReceivedInterestMap.get(projectInvestOrder.getId()));
				}
				dto.setInterest(interest);
				BigDecimal addInterest = BigDecimal.ZERO.setScale(2);
				if (null != projectInvestOrder.getPrizeId() && Byte.valueOf("8").equals(projectInvestOrder.getPrizeTypteId())) {// 有使用投资加息券
					if (null != prizeIdAddInterestMap.get(projectInvestOrder.getPrizeId())) {
						addInterest = addInterest.add(prizeIdAddInterestMap.get(projectInvestOrder.getPrizeId()));
					}
				}
				dto.setAddInterest(addInterest);
			} else {// 其他状态显示预期收益=预期利息+预期加息
				dto.setInterest(projectInvestOrder.getInterest());
				dto.setAddInterest(projectInvestOrder.getAddInterest());
			}
			if (ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue().equals(projectInvestOrder.getEvolutionStatus())
					|| ProjectInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
				if (null != investOrderIdMapCurrentPeriod.get(projectInvestOrder.getId())) {
					dto.setCurrentPeriod(investOrderIdMapCurrentPeriod.get(projectInvestOrder.getId()));
				} else {
					dto.setCurrentPeriod(1);
				}
			}

			if (ProjectInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
				Project project = projectMap.get(projectInvestOrder.getProjectId());
				if (project.getFullSuccessTime() != null && project.getFullSuccessTime().before(DateUtils.parseDateFromString("2018-03-06", DateUtils.SHOW_DATE_FORMAT))) {
					dto.setOverdueTip(overdueTypeMapperExt.selectByType(OverdueTypeEnum._24H_RULE.getCode().intValue()).getDescription());// "回款逾期提示"
				} else if (project.getFullSuccessTime() != null && (!project.getFullSuccessTime().before(DateUtils.parseDateFromString("2018-03-06", DateUtils.SHOW_DATE_FORMAT)))
						&& project.getFullSuccessTime().before(DateUtils.parseDateFromString("2018-09-01", DateUtils.SHOW_DATE_FORMAT))) {
					if (ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(project.getType())) {
						dto.setOverdueTip(overdueTypeMapperExt.selectByType(OverdueTypeEnum._16D_RULE.getCode().intValue()).getDescription());// "回款逾期提示"
					} else {
						dto.setOverdueTip(overdueTypeMapperExt.selectByType(OverdueTypeEnum._31D_RULE.getCode().intValue()).getDescription());// "回款逾期提示"
					}
				} else {
					dto.setOverdueTip(OverdueTypeEnum._10D_RULE.getDesc());// "回款逾期提示"
				}
			}
			dto.setContractUrl(projectIdMapContranctUrl.get(projectInvestOrder.getProjectId()));
			ProjectContract orderRiskContract = orderRiskTipsMap.get(projectInvestOrder.getId());
			if (orderRiskContract != null) {
				dto.setRiskTipsContractUrl(orderRiskContract.getViewUrl());
			}
			dto.setPublishTime(projectInvestOrder.getPublishTime());

			Integer successCount = userIdSuccessCountMap.get(dto.getBorrowerUserId());
			if (null != successCount) {
				if (successCount >= 1) {
					dto.setBorrowerLoanTimes(successCount - 1);
				} else {
					dto.setBorrowerLoanTimes(0);
				}
			}
			resultList.add(dto);
		}
		return resultList;
	}

	/**
	 * 新增可投标的
	 *
	 * @param projectInvesting
	 */
	public void addProjectInvesting(ProjectInvesting projectInvesting) {
		projectInvestingMapperExt.insertSelective(projectInvesting);
	}

	/**
	 * 从主库获取投资订单id
	 *
	 * @param investOrderId
	 * @return
	 */
	@Transactional(isolation = READ_COMMITTED)
	public ProjectInvestOrder getProjectInvestOrderFromMaster(Long investOrderId) {
		return projectInvestOrderMapperExt.selectByPrimaryKey(investOrderId);
	}

	public void addGroupBatch(Long projectId) {
		List<ProjectInvestOrderProcess> projectInvestOrderProcesses = projectInvestOrderProcessMapperExt.selectAddingGroupList(projectId);
		for (ProjectInvestOrderProcess projectInvestOrderProcess : projectInvestOrderProcesses) {
			Project project = projectMapperExt.selectByPrimaryKey(projectInvestOrderProcess.getProjectId());
			if (project.getSubStatus().equals(ProjectSubStatusEnum.COLLECTING_LENDING.getSubStatus()) || project.getStatus().equals(ProjectStatusEnum.REPAYING.getStatus())
					|| project.getStatus().equals(ProjectStatusEnum.FINISH.getStatus())) {
				this.addGroup(projectInvestOrderProcess.getTitle(), projectInvestOrderProcess.getProjectId(), projectInvestOrderProcess.getBorrowUserId(), projectInvestOrderProcess.getUserId(),
						projectInvestOrderProcess.getInvestOrderId());
			}
		}
	}

	/**
	 * 创建项目群
	 *
	 * @param title
	 * @param projectId
	 * @param borrowerUserId
	 * @param investorUserId
	 * @param investOrderId
	 */
	private void addGroup(String title, Long projectId, String borrowerUserId, String investorUserId, Long investOrderId) {
		try {
			Project project = projectMapperExt.selectByPrimaryKey(projectId);
			// 1为你我金融借款，2为暖心借借款(默认你我金融)
			String type = OrgConstant.NIWOTA_ORG_ID.equals(project.getOrgId()) ?  "2" : "1";

			ProjectGroupCreateRequest projectGroupCreateRequest = new ProjectGroupCreateRequest();
			projectGroupCreateRequest.setGroupName(title);
			projectGroupCreateRequest.setType(type);
			projectGroupCreateRequest.setProjectId(String.valueOf(projectId));
			projectGroupCreateRequest.setBusinessIdStr(borrowerUserId + "," + investorUserId);
			// roleId 借款人2 投资人3
			projectGroupCreateRequest.setRoleIdStr("2,3");
			CompletableFuture<IMCommonResponse> imCommonResponseCompletableFuture = imService.projectGroupCreate(projectGroupCreateRequest);
			IMCommonResponse imCommonResponse = imCommonResponseCompletableFuture.get();
			if (Integer.valueOf("1").equals(imCommonResponse.getCode())) {
				ProjectInvestOrderProcess projectInvestOrderProcessUpdater = new ProjectInvestOrderProcess();
				projectInvestOrderProcessUpdater.setInvestOrderId(investOrderId);
				projectInvestOrderProcessUpdater.setAddGroupStatus(Byte.valueOf("2"));
				projectInvestOrderProcessMapperExt.updateByPrimaryKeySelective(projectInvestOrderProcessUpdater);
				logger.info("创建项目群成功，investOrderId={}", investOrderId);
			} else {
				logger.error("创建项目群异常，investOrderId={},msg={} ", investOrderId, imCommonResponse.getMsg());
			}
		} catch (Exception e) {
			logger.error("创建项目群异常，investOrderId={}", investOrderId, e);
		}
	}

	/**
	 * 投资后置处理
	 *
	 * @param investOrderId
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void investPostProcess(Long investOrderId) {
		ProjectInvestOrder projectInvestOrder = projectInvestOrderMapperExt.selectByPrimaryKeyForUpdate(investOrderId);
		Long projectId = projectInvestOrder.getProjectId();
		if (OrderInvestTypeEnum.CREDITOR.getType().equals(projectInvestOrder.getInvestType())) {
			creditorSaleDetail(projectInvestOrder);
			return;
		}
		// 此处需要触发满标处理
		Project projectForUpdate = projectMapperExt.selectByPrimaryKeyForUpdate(projectId);
		if (ProjectStatusEnum.MISCARRY.getStatus().equals(projectForUpdate.getStatus())) {// 流标后撤投及投资红包
			this.investCancel(projectInvestOrder.getId(), InvestOrderCloseReasonEnum.INVEST_OVERTIME);
		} else if (ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus() == projectForUpdate.getSubStatus()) {// 判断是否满标
			BigDecimal investedAmount = projectInvestOrderMapperExt.selectInvestedAmount(projectId);
			if (projectForUpdate.getContractAmount().compareTo(investedAmount) == 0) {// 满标处理

				Project projectUpdater = new Project();
				projectUpdater.setBorrowUserId(projectForUpdate.getBorrowUserId());
				projectUpdater.setProjectId(projectId);
				projectUpdater.setSubStatus(ProjectSubStatusEnum.COLLECTING_LENDING.getSubStatus());// 筹款中-放款中
				projectUpdater.setFullSuccessTime(new Date());
				projectMapperExt.updateByPrimaryKeySelective(projectUpdater);

				// 更改投资中标的标的状态
				ProjectInvesting projectInvestingUpdater = new ProjectInvesting();
				projectInvestingUpdater.setProjectId(projectId);
				projectInvestingUpdater.setStatus(Byte.valueOf("3"));// 已售罄
				projectInvestingUpdater.setFullSuccessTime(new Date());
				projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvestingUpdater);

				try {
					if (BorrowProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectForUpdate.getType()) || BorrowProjectTypeEnum.DECORATION_LOAN.getValue().equals(projectForUpdate.getType())) {
						ProjectStatusChangeDTO projectStatusChangeDTO = new ProjectStatusChangeDTO();
						projectStatusChangeDTO.setProjectId(projectForUpdate.getProjectId());
						logger.info("send ORG_SPEEDLOAN_NEW LoanStatusDTO:{}", JSON.toJSON(projectStatusChangeDTO));
						rabbitTemplate.convertAndSend(OrgRabbitConstant.Exchange.ORG_COMMON_DIRECT_EXCHANGE,
								CardNiuExternalMQConstants.getRoutingKey(OrgRabbitConstant.Exchange.ORG_COMMON_DIRECT_EXCHANGE, OrgRabbitConstant.Queue.PROJECT_STATUS_CHANGE_NOTIFY_QUEUE),
								projectStatusChangeDTO);
					}
				} catch (Throwable e) {
					logger.error("send ORG_SPEEDLOAN_NEW LoanStatusDTO异常 projectId:{}", projectId, e);
				}

				rabbitTemplate.convertAndSend(MqConstant.PROJECT_WAIT_PUBLISH_TO_PUBLISH_EXCHANGE, MqConstant.PROJECT_WAIT_PUBLISH_TO_PUBLISH_ROUTING, projectId);

				// 构建放款订单
				AsyncTransactionRequest asyncTransactionRequest = projectLendLocalService.buildLendOrder(projectId);
				TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
					@Override
					public void afterCommit() {
						super.afterCommit();
						// 调用银行存管放款
						try {
							// 智能投状态特殊处理
							if (OrderInvestTypeEnum.INTELLIGENCE.getType().equals(projectInvestOrder.getInvestType())) {
								intelligenceInvestService.changProjectPackageStatusByProjectId(projectForUpdate.getProjectId());
							}
							projectLendLocalService.callBankLend(asyncTransactionRequest);
						} catch (Exception e) {
							logger.error("调用银行存管放款异常, projectId={}", projectId, e);
						}
					}
				});

				// 建项目群
				if(enableAddGroup(projectInvestOrder.getProjectType(), projectInvestOrder.getInvestType(), projectInvestOrder.getInvestorType(), projectForUpdate.getOrgId())) {
					this.addGroup(projectInvestOrder.getTitle(), projectId, projectInvestOrder.getBorrowUserId(), projectInvestOrder.getUserId(), projectInvestOrder.getId());

					addGroupBatch(projectId);
				}
			}
			// 投资成功通知
			try {
				// 智能投不发送单笔成功
				if (!OrderInvestTypeEnum.INTELLIGENCE.getType().equals(projectInvestOrder.getInvestType())) {
					sendInvestMessage(projectInvestOrder);
				}
				//修改昵称成功，发送MQ消息  -->用户是否新手任务
				NoviceTaskRecordRequestDTO dto = new NoviceTaskRecordRequestDTO();
				dto.setUserId(projectInvestOrder.getUserId());
				dto.setTaskType(NoviceTaskPrizeEnum.FRIST_INVEST.getCode());
				rabbitTemplate.convertAndSend(RabbitConstant.Exchange.NOVICE_TASK_RECORD, RabbitConstant.RoutingKey.NOVICE_TASK_RECORD, dto);
			} catch (Exception e) {
				logger.error("novice_task_activity发送投资成功通知异常,userId={},orderId={},projectId={}", projectInvestOrder.getUserId(), investOrderId, projectId, e);
			}
		}
		// 投资订单成功后置处理

		try {// 修改用户身份为投资人
			userBasicInfoDubboService.changeUserIdentityTag(projectInvestOrder.getUserId(), UserIdentityEnum.INVESTOR);
		} catch (Exception e) {
			logger.error("修改用户{}身份为投资人异常", projectInvestOrder.getUserId(), e);
		}

		try {
			InvestSuccessRequest investSuccessRequest = new InvestSuccessRequest();
			investSuccessRequest.setUserId(String.valueOf(projectInvestOrder.getUserId()));
			investSuccessRequest.setBid(String.valueOf(projectInvestOrder.getProjectId()));
			investSuccessRequest.setInvestOrderId(String.valueOf(projectInvestOrder.getId()));
			investSuccessRequest.setDfAmount(projectInvestOrder.getAmount());
			investSuccessRequest.setStartDate(new DateTime(projectInvestOrder.getCreateTime()).getMillis() * 1000);
			investSuccessRequest.setRate(projectInvestOrder.getBorrowRate());
			// 1月2天
			investSuccessRequest.setTotalPeriods(Byte.valueOf("1").equals(projectInvestOrder.getDeadlineUnit()) ? projectInvestOrder.getDeadline() : 1);
			investSuccessRequest.setReAmount(projectInvestOrder.getAmount());
			investSuccessRequest.setRevenue(projectInvestOrder.getInterest());
			investSuccessRequest.setIsAuto(Byte.valueOf("2").equals(projectInvestOrder.getType()) ? 1 : 0);
			investSuccessRequest.setUserPrizeId(projectInvestOrder.getPrizeId());
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INVEST_EVENT, RabbitConstant.RoutingKey.INVEST_EVENT, investSuccessRequest);
		} catch (Throwable e) {
			logger.error("投资事件（天秤）发送至mq异常，投资订单：{}", projectInvestOrder.getId(), e);
		}
		try {
			UserInvestEventDTO userInvestEventDTO = new UserInvestEventDTO();
			userInvestEventDTO.setInvestorId(projectInvestOrder.getId());
			userInvestEventDTO.setUserId(projectInvestOrder.getUserId());
			// 1:个人极速借 2:机构极速借 3:你我云贷
			Byte projectType = projectInvestOrder.getProjectType();
			com.niiwoo.activity.stage.enums.inviterBuddy.ProjectTypeEnum projectTypeEnum = com.niiwoo.activity.stage.enums.inviterBuddy.ProjectTypeEnum.findForValue(Integer.valueOf(projectType));
			userInvestEventDTO.setProjectId(projectInvestOrder.getProjectId());
			userInvestEventDTO.setProjectType(projectTypeEnum);
			userInvestEventDTO.setAmount(projectInvestOrder.getAmount());
			userInvestEventDTO.setDateTime(projectInvestOrder.getCreateTime());
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INVEST_EVENT_FOR_ACTIVITY, RabbitConstant.RoutingKey.INVEST_EVENT_FOR_ACTIVITY, userInvestEventDTO);
		} catch (Throwable e) {
			logger.error("投资事件（活动）发送至mq异常，投资订单：{}", projectInvestOrder.getId(), e);
		}
	}

	/**
	 * 债权投资后置处理
	 *
	 * @param projectInvestOrder
	 */
	private void creditorSaleDetail(ProjectInvestOrder projectInvestOrder) {

		AsyncTransactionRequest asyncTransactionRequest = creditorTransferConfirmLocalService.buildCreditorTransferConfirmOrder(projectInvestOrder.getCreditorSaleDetailId(),
				projectInvestOrder.getCreditorSaleUserId());
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCommit() {
				super.afterCommit();
				// 调用银行存管放款
				try {
					creditorTransferConfirmLocalService.callBankCreditorTransferConfirm(projectInvestOrder.getCreditorSaleDetailId(), asyncTransactionRequest);
				} catch (Exception e) {
					logger.error("调用银行存管放款异常, 投资订单ID={}", projectInvestOrder.getId(), e);
				}
			}
		});

		// 通知/push/短信
		tradableInvestOrderService.sendDebtTransferSuccessMessage(projectInvestOrder);

		try {// 修改用户身份为投资人
			userBasicInfoDubboService.changeUserIdentityTag(projectInvestOrder.getUserId(), UserIdentityEnum.INVESTOR);
		} catch (Exception e) {
			logger.error("修改用户{}身份为投资人异常", projectInvestOrder.getUserId(), e);
		}
	}

	private void sendInvestMessage(ProjectInvestOrder investOrder) {
		UserLetterRequestDTO req = new UserLetterRequestDTO();
		req.setToUserId(investOrder.getUserId());
		req.setUserLetterTypeEnum(UserLetterTypeEnum.ONLY_LETTER);
		req.setLetterTemplateEnum(MessageTemplateKeyEnum.LetterTemplateKeyEnum.INVEST_SUCCEED);
		String investTime = timeFormat.format(investOrder.getCreateTime());
		req.setLetterTemplateParamList(Lists.newArrayList(investTime, investOrder.getProjectId(), investOrder.getTitle()));
		RpcContext.getContext().asyncCall(() -> userLetterDubboService.pushLetter(req));
	}

	/**
	 * 红包撤销使用
	 *
	 * @param id
	 * @param prizeId
	 * @param userId
	 */
	private void resetPrizeToReturned(Long id, Long prizeId, String userId, Byte investType, Long investOrderId) {
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCommit() {
				super.afterCommit();
				try {
					ActivityPrizeResetStatusReqDTO activityPrizeResetStatusReqDTO = new ActivityPrizeResetStatusReqDTO();
					activityPrizeResetStatusReqDTO.setPrizeId(prizeId);
					activityPrizeResetStatusReqDTO.setUserId(userId);
					// 智能投需要额外的增加投资订单参数
					if (OrderInvestTypeEnum.INTELLIGENCE.getType().equals(investType)) {
						activityPrizeResetStatusReqDTO.setInvestOrderId(investOrderId);
					}
					try {
						activityPrizeDubboService.resetPrizeToReturned(activityPrizeResetStatusReqDTO);
					} catch (BizException e) {
						if (!"ACTSTAGE10025".equals(e.getErrorCode())) {
							throw e;
						}
					}
					ProjectInvestPrize projectInvestPrize = new ProjectInvestPrize();
					projectInvestPrize.setId(id);
					projectInvestPrize.setStatus(Byte.valueOf("2"));
					projectInvestPrizeMapperExt.updateByPrimaryKeySelective(projectInvestPrize);
				} catch (Exception e) {
					logger.error("投资红包撤销失败,prizeId={}，计划任务确保事务最终一致性", prizeId, JSON.toJSONString(e));
				}
			}
		});
	}

	/**
	 * 流标撤投，返还投资红包
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void investCancel(Long investOrderId, InvestOrderCloseReasonEnum closeReason) {
		ProjectInvestOrder projectInvestOrder = projectInvestOrderMapperExt.selectByPrimaryKeyForUpdate(investOrderId);
		if (Byte.valueOf("1").equals(projectInvestOrder.getStatus()) && !ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
			ProjectInvestCancelOrder projectInvestCancelOrder = projectInvestCancelOrderMapperExt.selectByPrimaryKey(investOrderId);
			if (null == projectInvestCancelOrder) {
				projectInvestCancelOrder = new ProjectInvestCancelOrder();
				projectInvestCancelOrder.setId(investOrderId);
				projectInvestCancelOrder.setUserId(projectInvestOrder.getUserId());
				projectInvestCancelOrder.setAccountId(projectInvestOrder.getAccountId());
				projectInvestCancelOrder.setProjectId(projectInvestOrder.getProjectId());
				projectInvestCancelOrder.setProjectType(projectInvestOrder.getProjectType());
				projectInvestCancelOrder.setProjectTitle(projectInvestOrder.getTitle());
				projectInvestCancelOrder.setDeadline(projectInvestOrder.getDeadline());
				projectInvestCancelOrder.setDeadlineUnit(projectInvestOrder.getDeadlineUnit());
				projectInvestCancelOrder.setProjectNo(String.valueOf(projectInvestOrder.getProjectId()));
				projectInvestCancelOrder.setAmount(projectInvestOrder.getAmount());
				projectInvestCancelOrder.setCreateTime(new Date());
				projectInvestCancelOrderMapperExt.insertSelective(projectInvestCancelOrder);
			}

			if (null != projectInvestOrder.getPrizeId()) {
				ProjectInvestPrize projectInvestPrize = null;
				if (OrderInvestTypeEnum.INTELLIGENCE.getType().equals(projectInvestOrder.getInvestType())) {
					projectInvestPrize = projectInvestPrizeMapperExt.selectByPrizeIdAndTypeAndInvestId(projectInvestOrder.getPrizeId(), Byte.valueOf("2"), investOrderId);
				} else {
					projectInvestPrize = projectInvestPrizeMapperExt.selectByPrizeIdAndType(projectInvestOrder.getPrizeId(), Byte.valueOf("2"));
				}
				if (null == projectInvestPrize) {
					projectInvestPrize = new ProjectInvestPrize();
					projectInvestPrize.setId(snowflakeIdWorker.nextId());
					projectInvestPrize.setUserId(projectInvestOrder.getUserId());
					projectInvestPrize.setProjectId(projectInvestOrder.getProjectId());
					projectInvestPrize.setInvestOrderId(projectInvestOrder.getId());
					projectInvestPrize.setPrizeId(projectInvestOrder.getPrizeId());
					projectInvestPrize.setType(Byte.valueOf("2"));
					projectInvestPrize.setCreateTime(new Date());
					projectInvestPrize.setInvestType(projectInvestOrder.getInvestType());
					projectInvestPrizeMapperExt.insertSelective(projectInvestPrize);
					this.resetPrizeToReturned(projectInvestPrize.getId(), projectInvestPrize.getPrizeId(), projectInvestPrize.getUserId(), projectInvestOrder.getInvestType(),
							projectInvestOrder.getId());
				}
			}
			// 撤投
			final ProjectInvestCancelOrder finalOrder = projectInvestCancelOrder;
			this.investCancelCallAccount(finalOrder, closeReason);
		}
	}

	/**
	 * 调用账户中心撤销投资
	 *
	 * @param finalOrder
	 */
	public void investCancelCallAccount(final ProjectInvestCancelOrder finalOrder, InvestOrderCloseReasonEnum closeReason) {
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCommit() {
				try {
					super.afterCommit();
					ProjectInvestCancelOrderDTO projectInvestCancelOrderDTO = new ProjectInvestCancelOrderDTO();
					projectInvestCancelOrderDTO.setId(finalOrder.getId());
					projectInvestCancelOrderDTO.setUserId(finalOrder.getUserId());
					projectInvestCancelOrderDTO.setAccountId(finalOrder.getAccountId());
					projectInvestCancelOrderDTO.setProjectId(finalOrder.getProjectId());
					projectInvestCancelOrderDTO.setProjectType(finalOrder.getProjectType());
					projectInvestCancelOrderDTO.setProjectTitle(finalOrder.getProjectTitle());
					projectInvestCancelOrderDTO.setDeadline(finalOrder.getDeadline());
					projectInvestCancelOrderDTO.setDeadlineUnit(finalOrder.getDeadlineUnit());
					projectInvestCancelOrderDTO.setProjectNo(finalOrder.getProjectNo());
					projectInvestCancelOrderDTO.setAmount(finalOrder.getAmount());
					projectInvestCancelOrderDTO.setReason(closeReason.getValue());
					accountInvestDubboService.projectInvestCancel(projectInvestCancelOrderDTO);
				} catch (Exception e) {
					logger.error("投资订单存管撤销失败,investOrderId={}，计划任务确保事务最终一致性", finalOrder.getId(), JSON.toJSONString(e));
				}

			}
		});
	}

	/**
	 * 投资解冻成功
	 *
	 * @param investOrderId
	 */
	public void investCancelSuccess(Long investOrderId, InvestOrderCloseReasonEnum closeReason) {
		investCommonServer.investCancelSuccess(investOrderId, closeReason);
	}

	/**
	 * 流标后已经成功投资订单撤投
	 *
	 * @param projectId
	 */
	@Transactional(isolation = READ_COMMITTED)
	public void investCancelForProject(Long projectId, int subStatus) {
		projectInvestingMapperExt.deleteByPrimaryKey(projectId);

		List<ProjectInvestOrder> projectInvestOrders = projectInvestOrderMapperExt.selectInvestedList(projectId);
		for (ProjectInvestOrder projectInvestOrder : projectInvestOrders) {
			if (ProjectSubStatusEnum.MISCARRY_CANCER_MANAGER.getSubStatus() == subStatus || ProjectSubStatusEnum.MISCARRY_LOAN_BATCH.getSubStatus() == subStatus) {
				this.investCancel(projectInvestOrder.getId(), InvestOrderCloseReasonEnum.MANAGER_CANCEL);
			} else {
				this.investCancel(projectInvestOrder.getId(), InvestOrderCloseReasonEnum.INVEST_OVERTIME);
			}
		}
	}

	/**
	 * 获取正在处理中的撤投订单
	 *
	 * @return
	 */
	public List<ProjectInvestCancelOrder> getInvestCancelingList() {
		return projectInvestCancelOrderMapperExt.selectInvestCancelingList();
	}

	@Transactional
	public void cleanAllBatchInvestSetting() {
		// userInvestConfigMapperExt.deleteAllByInvestWay(InvestWayEnum.BATCH.getValue());
		userInvestConfigMapperExt.systemCloseBatchInvest();
	}

	@Transactional
	public void signCommissionAuthAgreement(Long contractId) throws Exception {
		ProjectContract contract = projectContractMapperExt.selectByPrimaryKey(contractId);
		Byte contractType = contract.getContractType();
		FadadaContractTemplateEnum.TemplateTypeEnum typeEnum = FadadaContractTemplateEnum.TemplateTypeEnum.getByType(contractType);
		ProjectContract signContract = fadadaContractContext.contractHandler(typeEnum, contract.getContractNo(), null, contract.getUserId());
		signContract.setId(contract.getId());
		updateCommissionAuthAgreement(signContract);
	}

	@Transactional
	public ProjectContract initRiskTipsAgreement(Long investOrderId) {
		ProjectInvestOrder order = projectInvestOrderMapperExt.selectByPrimaryKey(investOrderId);
		Long projectId = order.getProjectId();
		String userId = order.getUserId();
		String format = dayFormat.format(new Date());
		String key = RISK_TIPS_CONTRACT_SEQ + format;
		Long value = redisTemplate.opsForValue().increment(key, 1);
		if (value == 1) {
			redisTemplate.expire(key, 24, TimeUnit.HOURS);
		}
		String paddingValue = StringHelper.lpadding(4, value);
		// NWFXTS + YYYYMMDD + 4位序号(0001)
		String contractNo = "NWFXTS" + format + paddingValue;
		Byte contractType = FadadaContractTemplateEnum.TemplateTypeEnum.RISK_PROMPT.getType();
		/**
		 * 1-批量投资,2-自动投资,3-手动投资 {@link InvestWayEnum}
		 */
		InvestWayEnum investWay = InvestWayEnum.enumOf(order.getType());
		FadadaContractEnum.TradeTypeEnum tradeType = FadadaContractEnum.TradeTypeEnum.INVEST;
		switch (investWay) {
			case AUTO:
				tradeType = FadadaContractEnum.TradeTypeEnum.AUTO_INVEST;
				break;
			case BATCH:
				tradeType = FadadaContractEnum.TradeTypeEnum.BATCH_INVEST;
				break;
			case MANUAL_DIRECT:
				tradeType = FadadaContractEnum.TradeTypeEnum.INVEST;
				break;
			case MANUAL_GATEWAY:
				tradeType = FadadaContractEnum.TradeTypeEnum.INVEST;
				break;
			default:
				logger.warn("no tradeType defined for investWay:{}", investWay);
				break;
		}
		ProjectContract contract = fadadaContractContext.initContractRecord(projectId, userId, contractNo, contractType, tradeType.getType(), investOrderId);
		projectContractMapperExt.insertSelective(contract);
		return contract;
	}

	@Transactional
	public void signRiskTipsAgreement(ProjectContract contract) throws Exception {
		Long projectId = contract.getProjectId();
		Project project = projectMapperExt.selectByPrimaryKey(projectId);
		Byte contractType = contract.getContractType();
		FadadaContractTemplateEnum.TemplateTypeEnum typeEnum = FadadaContractTemplateEnum.TemplateTypeEnum.getByType(contractType);
		ProjectContract signContract = fadadaContractContext.contractHandler(typeEnum, contract.getContractNo(), project, contract.getUserId());
		signContract.setId(contract.getId());
		updateCommissionAuthAgreement(signContract);
	}

	@Transactional
	public void transferSignRiskTipsAgreement(ProjectInvestOrder order) throws Exception {
		log.info("transferSignRiskTipsAgreement order={}", JSON.toJSONString(order));
		CreditorSaleDetail creditorSaleDetail = creditorSaleDetailMapperExt.selectByPrimaryKeyAndSaleUserId(order.getCreditorSaleDetailId(), order.getCreditorSaleUserId());
		String contractNo = "ZQ" + FastDateFormat.getInstance("yyMMdd").format(new Date()) + creditorSaleDetail.getId();

		ProjectContract contract = fadadaContractContext.initContractRecord(creditorSaleDetail.getId(), creditorSaleDetail.getUserId(), contractNo,
				FadadaContractTemplateEnum.TemplateTypeEnum.CREDITOR_TRANSFER_RISK_PROMPT.getType(), FadadaContractEnum.TradeTypeEnum.INVEST.getType(), creditorSaleDetail.getId());
		projectContractMapperExt.insertSelective(contract);

		FadadaContractTemplateEnum.TemplateTypeEnum typeEnum = FadadaContractTemplateEnum.TemplateTypeEnum.getByType(contract.getContractType());
		ProjectContract transferSignContract = creditorTransferRiskPrompt.contractHandler(typeEnum, order, creditorSaleDetail);
		transferSignContract.setId(contract.getId());
		updateCommissionAuthAgreement(transferSignContract);
	}

	/**
	 * 修改法大大委托授权协议书签署信息
	 *
	 * @param contract
	 *            委托授权协议书
	 */
	@Transactional
	public void updateCommissionAuthAgreement(ProjectContract contract) {
		projectContractMapperExt.updateByPrimaryKeySelective(contract);
	}

	/**
	 * 法大大委托授权协议书历史记录
	 */
	public PageResponseDTO<CommissionAuthAgreementResponseDTO> queryCommissionAuthHistory(String userId, Byte tradeType, Integer offset, Integer size) {
		PageResponseDTO<CommissionAuthAgreementResponseDTO> responseDTO = new PageResponseDTO<>();
		Byte contractType = FadadaContractTemplateEnum.TemplateTypeEnum.CONSIGNOR_AUTHORIZE_PROTOCOL.getType();
		int count = projectContractMapperExt.selectCommissionAuthCountByType(userId, contractType, tradeType);
		if (count > 0) {
			List<ProjectContract> contractList = projectContractMapperExt.queryByPageWithUserAndType(userId, contractType, tradeType, offset, size);
			List<CommissionAuthAgreementResponseDTO> dtoList = contractList.stream().map(contract -> {
				CommissionAuthAgreementResponseDTO dto = new CommissionAuthAgreementResponseDTO();
				dto.setId(contract.getId());
				dto.setAuthTime(contract.getCreateTime());
				dto.setContractNo(contract.getContractNo());
				dto.setSignStatus(CommissionAuthAgreementResponseDTO.SignStatus.enumOf(contract.getStatus()));
				dto.setViewUrl(contract.getViewUrl());
				dto.setDownloadUrl(contract.getDownloadUrl());
				return dto;
			}).collect(Collectors.toList());
			responseDTO.setItems(dtoList);
		}
		responseDTO.measureTotalPage(count, size);
		return responseDTO;
	}

	public boolean isSelfBorrowProject(String investorId, ProjectInvesting project) {
		return investorId.equals(project.getBorrowUserId());
	}

	/**
	 * 使用 mq 当前投资金额已达该风险类型投资上限处理
	 */
	public void notifyInvestStopForEvaluation(String userId, InvestFromTypeEnum investTypeEnum) {
		log.info("投资限额###当前投资金额已达该风险类型投资上限处理准备发送MQ, userId={}, investTypeEnum={}", userId, investTypeEnum.getDesc());
		InvestStopForEvaluationDTO investStopForEvaluationDTO = new InvestStopForEvaluationDTO();
		investStopForEvaluationDTO.setUserId(userId);
		investStopForEvaluationDTO.setInvestTypeEnum(investTypeEnum);
		try {
			rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INVEST_STOP_BY_EVALUATION_DEAL, RabbitConstant.RoutingKey.INVEST_STOP_BY_EVALUATION_DEAL, investStopForEvaluationDTO);
		} catch (Exception e) {
			log.error("投资限额###当前投资金额已达该风险类型投资上限处理发送MQ失败###investStopForEvaluationDTO={}", JSON.toJSONString(investStopForEvaluationDTO), e);
		}
	}

	public ProjectInvestOrder queryInvestOrder(Long orderId) {
		return projectInvestOrderMapperExt.selectByPrimaryKey(orderId);
	}

	@Transactional
	public void resetExpiredTime(Long id, Date expired) {
		ProjectInvestOrder order = new ProjectInvestOrder();
		order.setId(id);
		order.setExpireTime(expired);
		projectInvestOrderMapperExt.updateByPrimaryKeySelective(order);
	}

	@Transactional
	public void cancelInvestOrderLocal(Long investOrderId, InvestOrderCloseReasonEnum closeReason) {
		ProjectInvestOrder order = new ProjectInvestOrder();
		order.setId(investOrderId);
		order.setStatus(ProjectInvestOrderStatusEnum.CANCEL.getValue());
		order.setClosedReason(closeReason.getValue());
		projectInvestOrderMapperExt.cancelDealingOrder(order);
	}

	/**
	 * 获取标的推荐信息(按照配置顺序)
	 *
	 * @return
	 */
	public RecommendInvestResponseDTO queryRecommendInvest() {
		// 期限配置
		List<HomePageRecomConfig> configList = homePageRecomConfigMapperExt.findLastData();
		if (Objects.isNull(configList)) {
			throw new BizException("TRD1100001");
		}
		configList = configList.stream().filter(homePageRecomConfig -> homePageRecomConfig.getType() ==
				HomePageRecomConfigEnum.NORMAL_INTELLIGENCE.getType() || homePageRecomConfig.getType() ==
				HomePageRecomConfigEnum.PROJECT.getType()).collect(Collectors.toList());
		return getRecommendInvestResponseDTO(configList);
	}

	private RecommendInvestResponseDTO getRecommendInvestResponseDTO(List<HomePageRecomConfig> configList) {

		// 先查询可投资的 -- 后台可能配置多条，当第一条找不到的时候，就用第二条，
		for (HomePageRecomConfig config : configList) {
			// 智能投
			if (HomePageRecomConfigEnum.NORMAL_INTELLIGENCE.getType().equals(config.getType())) {

				Byte packageType = PackageTypeEnum.NORMAL_INTELLIGENCE.getType();

				ProjectPackage projectPackage = projectPackageMapperExt.selectByDeadlineAndStatus(config.getDeadlineUnit(), config.getDeadline(), ProjectPackageStatusEnum.INVESTING.getStatus(),
						config.getSortOrder(), config.getRepaymentType(), packageType);// 包类型(1：普通智能投，2：新手智能投)
				if (Objects.isNull(projectPackage)) {
					continue;
				}
				String labelInfo = HomePageRecomConfigEnum.HomePageLabelEnum.NORMAL_INTELLIGENCE.getDesc();
				return generateInvestResponseDTO(projectPackage, labelInfo,(byte)0);
			} else {

				String labelProjectInfo = HomePageRecomConfigEnum.HomePageLabelEnum.PROJECT.getDesc();

				ProjectInvesting projectInvesting = projectInvestingMapperExt.selectByDeadlineAndStatus(config.getDeadlineUnit(), config.getDeadline(),
						InvestableProjectStatusEnum.INVESTING.getValue(), config.getOrgId(), config.getSortOrder(), config.getRepaymentType(), null);
				if (Objects.isNull(projectInvesting)) {
					continue;
				}
				return generateRecommendInvestResponseDTO(projectInvesting, labelProjectInfo,(byte)0);

			}

		}
		String labelProjectInfo = HomePageRecomConfigEnum.HomePageLabelEnum.PROJECT.getDesc();
		// 无可投资的标的 再查询满标的
		for (HomePageRecomConfig config : configList) {
			ProjectInvesting projectInvesting = projectInvestingMapperExt.selectByDeadlineAndStatus(config.getDeadlineUnit(), config.getDeadline(), null, config.getOrgId(), config.getSortOrder(),
					config.getRepaymentType(), null);
			if (Objects.isNull(projectInvesting)) {
				continue;
			}
			return generateRecommendInvestResponseDTO(projectInvesting, labelProjectInfo,(byte)0);
		}
		// 无数据就在订单表查询
		for (HomePageRecomConfig config : configList) {
			Project project = projectMapperExt.selectLastByDeadline(config.getDeadlineUnit(), config.getDeadline(), String.valueOf(config.getOrgId()), config.getRepaymentType());
			if (Objects.isNull(project)) {
				continue;
			}
			return generateInvestResponseDTO(project, labelProjectInfo,(byte)0);
		}
		Project project = projectMapperExt.selectLastFullTime();
		return generateInvestResponseDTO(project, labelProjectInfo,(byte)0);
	}

	/**
	 * 优先推荐新手智能投和新手标
	 *
	 * @return
	 */
	public RecommendInvestResponseDTO queryNoviceRecommendInvest() {
		// 期限配置
		List<HomePageRecomConfig> configList = homePageRecomConfigMapperExt.findLastData();
		if (Objects.isNull(configList)) {
			throw new BizException("TRD1100001");
		}
		String labelInfo = HomePageRecomConfigEnum.HomePageLabelEnum.NOVICE.getDesc();
		// 如果配置了新手智能投则查找
		List<HomePageRecomConfig> listPackage = configList.stream().filter(homePageRecomConfig -> homePageRecomConfig.getType() == HomePageRecomConfigEnum.NOVICE_INTELLIGENCE.getType())
				.collect(Collectors.toList());
		if (CollectionUtils.isNotEmpty(listPackage)) {
			for (HomePageRecomConfig config : listPackage) {
				ProjectPackage projectPackage = projectPackageMapperExt.selectByDeadlineAndStatus(config.getDeadlineUnit(), config.getDeadline(), ProjectPackageStatusEnum.INVESTING.getStatus(),
						config.getSortOrder(), config.getRepaymentType(), PackageTypeEnum.NOVICE_INTELLIGENCE.getType());// 包类型(1：普通智能投，2：新手智能投)
				if (Objects.isNull(projectPackage)) {
					continue;
				}
				return generateInvestResponseDTO(projectPackage, labelInfo,(byte)1);
			}
		}
		// 如果配置新手标
		String labelInfoProject = HomePageRecomConfigEnum.HomePageLabelEnum.NOVICE_PROJECT.getDesc();
		NewbieProjectConfig newbieProjectConfig = newbieProjectConfigMapper.selectNewbieProjectConfig(null);
		if (newbieProjectConfig != null) {
			String channelCode = newbieProjectConfig.getChannelCode();
			List<HomePageRecomConfig> listProject = configList.stream().filter(homePageRecomConfig -> homePageRecomConfig.getType() == HomePageRecomConfigEnum.NOIVCE_PROJECT.getType())
					.collect(Collectors.toList());
			if (CollectionUtils.isNotEmpty(listProject)) {
				for (HomePageRecomConfig config : listProject) {
					ProjectInvesting projectInvesting = projectInvestingMapperExt.selectByDeadlineAndStatus(config.getDeadlineUnit(), config.getDeadline(),
							InvestableProjectStatusEnum.INVESTING.getValue(), config.getOrgId(), config.getSortOrder(), config.getRepaymentType(), channelCode);
					if (Objects.isNull(projectInvesting)) {
						continue;
					}
					return generateRecommendInvestResponseDTO(projectInvesting, labelInfoProject,(byte)1);
				}
			}
		}
		// 如果什么都没有，那就只能按照正常配置的推荐顺序查找
		List<HomePageRecomConfig> normalConfigList = configList.stream().filter(homePageRecomConfig -> homePageRecomConfig.getType() ==
				HomePageRecomConfigEnum.NORMAL_INTELLIGENCE.getType() || homePageRecomConfig.getType() ==
				HomePageRecomConfigEnum.PROJECT.getType()).collect(Collectors.toList());
		return getRecommendInvestResponseDTO(normalConfigList);
	}

	private RecommendInvestResponseDTO generateInvestResponseDTO(ProjectPackage projectPackage, String desc,byte isNewProject) {
		RecommendInvestResponseDTO response = new RecommendInvestResponseDTO();
		response.setType((byte) 2);
		response.setProjectAttribute(2);
		response.setIsNewProject(isNewProject);
		response.setLabelInformation(desc);
		ProjectInfoResponseDTO projectInfoResponse = new ProjectInfoResponseDTO();
		response.setProjectInfo(projectInfoResponse);
		projectInfoResponse.setBorrowRate(projectPackage.getBorrowRate());
		projectInfoResponse.setContractAmount(projectPackage.getAmount());
		projectInfoResponse.setDeadline(projectPackage.getDeadline());
		projectInfoResponse.setDeadlineUnit(projectPackage.getDeadlineUnit());
		projectInfoResponse.setInvestingAmount(projectPackage.getAmount().subtract(projectPackage.getInvestedAmount()));
		projectInfoResponse.setIsSoldOut(ProjectPackageStatusEnum.INVESTING.getStatus().equals(projectPackage.getStatus()) ? (byte) 0 : (byte) 1);
		projectInfoResponse.setProjectId(projectPackage.getPackageId());
		projectInfoResponse.setRepaymentTypeDesc(com.niiwoo.civet.trade.enums.RepaymentTypeEnum.enumOf(projectPackage.getRepaymentType()).getDesc());
		projectInfoResponse.setActivityTitle(projectPackage.getActivityLabel());
		projectInfoResponse.setAddInerest(projectPackage.getActivityRaiseRate());
		return response;
	}

	private RecommendInvestResponseDTO generateInvestResponseDTO(Project project, String desc,byte isNewProject) {
		RecommendInvestResponseDTO response = new RecommendInvestResponseDTO();
		response.setType((byte) 2);
		response.setProjectAttribute(1);
		response.setIsNewProject(isNewProject);
		response.setLabelInformation(desc);
		ProjectInfoResponseDTO projectInfoResponse = new ProjectInfoResponseDTO();
		response.setProjectInfo(projectInfoResponse);
		if (Objects.nonNull(project)) {
			projectInfoResponse.setBorrowRate(project.getBorrowRate());
			projectInfoResponse.setBorrowUserId(project.getBorrowUserId());
			projectInfoResponse.setContractAmount(project.getContractAmount());
			projectInfoResponse.setDeadline(project.getDeadline());
			projectInfoResponse.setDeadlineUnit(project.getDeadlineUnit());
			projectInfoResponse.setInvestingAmount(BigDecimal.ZERO);
			projectInfoResponse.setIsSoldOut((byte) 1);
			projectInfoResponse.setProjectId(project.getProjectId());
			projectInfoResponse.setRepaymentTypeDesc(com.niiwoo.civet.trade.enums.RepaymentTypeEnum.enumOf(project.getRepaymentType()).getDesc());
			if (Objects.nonNull(project.getActivityId())) {
				InvestAddInterestRateConfig config = investAddInterestRateConfigMapperExt.selectByPrimaryKey(project.getActivityId());
				projectInfoResponse.setActivityTitle(config.getTitle());
				projectInfoResponse.setAddInerest(project.getActivityRaiseRate());
			}
		}
		return response;
	}

	private RecommendInvestResponseDTO generateRecommendInvestResponseDTO(ProjectInvesting projectInvesting, String desc,byte isNewProject) {
		RecommendInvestResponseDTO response = new RecommendInvestResponseDTO();
		response.setType((byte) 2);
		response.setProjectAttribute(1);
		response.setIsNewProject(isNewProject);
		response.setLabelInformation(desc);
		ProjectInfoResponseDTO projectInfoResponse = new ProjectInfoResponseDTO();
		response.setProjectInfo(projectInfoResponse);
		projectInfoResponse.setBorrowRate(projectInvesting.getBorrowRate());
		projectInfoResponse.setBorrowUserId(projectInvesting.getBorrowUserId());
		projectInfoResponse.setContractAmount(projectInvesting.getContractAmount());
		projectInfoResponse.setDeadline(projectInvesting.getDeadline());
		projectInfoResponse.setDeadlineUnit(projectInvesting.getDeadlineUnit());
		projectInfoResponse.setInvestingAmount(projectInvesting.getContractAmount().subtract(projectInvesting.getInvestedAmount()));
		projectInfoResponse.setIsSoldOut(InvestableProjectStatusEnum.INVESTING.getValue().equals(projectInvesting.getStatus()) ? (byte) 0 : (byte) 1);
		projectInfoResponse.setProjectId(projectInvesting.getProjectId());
		projectInfoResponse.setRepaymentTypeDesc(com.niiwoo.civet.trade.enums.RepaymentTypeEnum.enumOf(projectInvesting.getRepaymentType()).getDesc());
		if (Objects.nonNull(projectInvesting.getActivityId())) {
			InvestAddInterestRateConfig config = investAddInterestRateConfigMapperExt.selectByPrimaryKey(projectInvesting.getActivityId());
			projectInfoResponse.setActivityTitle(config.getTitle());
			projectInfoResponse.setAddInerest(projectInvesting.getActivityRaiseRate());
		}
		return response;
	}

	/**
	 * H5页面散列表筛选查询
	 *
	 * 没有做后台配置，先写死
	 *
	 * @return
	 */
	public FilterInvestConfigDTO queryNormalInvestingSelector() {

		FilterInvestConfigDTO result = new FilterInvestConfigDTO();

		List<FilterInvestConfigDTO.Configure> filterType = new ArrayList<>();

		FilterInvestConfigDTO.Configure configure = new FilterInvestConfigDTO.Configure();
		configure.setName("全部");
		filterType.add(configure);

		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("抵押标");
		configure.setValue(2);
		filterType.add(configure);

		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("信用标");
		configure.setValue(1);
		filterType.add(configure);

		result.setFilterType(filterType);

		List<FilterInvestConfigDTO.Configure> repaymentType = new ArrayList<>();
		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("全部");
		repaymentType.add(configure);

		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("先息后本");
		configure.setValue(RepaymentTypeEnum.MONTHLY_INTEREST_PAYMENT_DUE.getValue());
		repaymentType.add(configure);
		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("等额本息");
		configure.setValue(RepaymentTypeEnum.AVERAGE_CAPITAL_PLUS_INTEREST.getValue());
		repaymentType.add(configure);
		configure = new FilterInvestConfigDTO.Configure();
		configure.setName("到期本息");
		configure.setValue(RepaymentTypeEnum.REPAY_PRINCIPAL_INTEREST_AT_TERM.getValue());
		repaymentType.add(configure);
		result.setRepaymentType(repaymentType);

		List<FilterInvestConfigDTO.Configure> periodType = new ArrayList<>();

		for (FiterTypeEnum.PeriodTypeEnum periodTypeEnum : FiterTypeEnum.PeriodTypeEnum.values()) {
			configure = new FilterInvestConfigDTO.Configure();
			if (!FiterTypeEnum.PeriodTypeEnum.ALL.getTitle().equals(periodTypeEnum.getTitle())) {
				FilterInvestConfigDTO.Config periodConfig = new FilterInvestConfigDTO.Config();
				periodConfig.setTitle(periodTypeEnum.getTitle());
				periodConfig.setTermUnit(periodTypeEnum.getTerm());
				periodConfig.setMinval(periodTypeEnum.getBeginValue());
				periodConfig.setMinvalCalSymbol(periodTypeEnum.getCalSymbol());
				periodConfig.setMaxval(periodTypeEnum.getEndValue());
				periodConfig.setMaxvalCalSymbol(periodTypeEnum.getCalSymbol());
				configure.setValue(periodConfig);
			}
			configure.setName(periodTypeEnum.getTitle());
			periodType.add(configure);
		}
		result.setPeriodType(periodType);

		List<FilterInvestConfigDTO.Configure> rateType = new ArrayList<>();
		for (FiterTypeEnum.RateTypeEnum rateTypeEnum : FiterTypeEnum.RateTypeEnum.values()) {
			configure = new FilterInvestConfigDTO.Configure();
			if (!FiterTypeEnum.RateTypeEnum.ALL.getTitle().equals(rateTypeEnum.getTitle())) {
				FilterInvestConfigDTO.Config rateConfig = new FilterInvestConfigDTO.Config();
				rateConfig.setTitle(rateTypeEnum.getTitle());
				rateConfig.setMinval(rateTypeEnum.getBeginValue());
				rateConfig.setMinvalCalSymbol(rateTypeEnum.getCalSymbol());
				rateConfig.setMaxval(rateTypeEnum.getEndValue());
				rateConfig.setMaxvalCalSymbol(rateTypeEnum.getCalSymbol());
				configure.setValue(rateConfig);
			}
			configure.setName(rateTypeEnum.getTitle());
			rateType.add(configure);
		}
		result.setRateType(rateType);

		return result;
	}

	/**
	 * 债权转让协议-受让人预览
	 *
	 * @param creditorSaleId
	 *            出让订单ID
	 * @param transferUserId
	 *            出让人用户ID
	 * @param userId
	 *            受让人用户ID
	 * @return CreditorTransferProtocolPreviewResponseDTO
	 */
	public CreditorTransferProtocolPreviewResponseDTO creditorTransferProtocolPreview(Long creditorSaleId, String transferUserId, String userId) {
		log.info("creditorTransferProtocolPreview creditorSaleId={}, transferUserId={}, investorUserId={}", creditorSaleId, transferUserId, userId);

		CreditorTransferProtocolPreviewResponseDTO responseDTO = new CreditorTransferProtocolPreviewResponseDTO();
		CreditorSaleDetail creditorSaleDetail = creditorSaleDetailMapperExt.selectByPrimaryKeyAndSaleUserId(creditorSaleId, transferUserId);
		log.info("creditorTransferProtocolPreview creditorSaleDetail={}", JSON.toJSONString(creditorSaleDetail));
		tradableInvestOrderService.reCalcAmount(creditorSaleDetail);
		Project project = projectMapperExt.selectByPrimaryKey(creditorSaleDetail.getProjectId());

		responseDTO.setProtocolNo("ZQ" + FastDateFormat.getInstance("yyMMdd").format(new Date()) + creditorSaleId);
		responseDTO.setNiiwooReditCode(privacyMasks.maskInstitutionalReditCode(PlateformConstant.NIIWOO_REDIT_CODE));
		responseDTO.setNiiwooAddress("广东省深圳市南山区科技园北区乌石头路8号天明科技大厦主楼11楼、12楼1202室");
		responseDTO.setBorrowerName(project.getBorrowUserName());
		responseDTO.setBorrowerNickName(project.getBorrowNickName());

		if (BorrowerTypeEnum.COMPANY.getType().equals(project.getBorrowerType())) {
			SimpleOrgInfo borrowOrg = organizationDubboService.getByOrgId(project.getBorrowUserId());
			responseDTO.setBorrowerIdCard(
					privacyMasks.maskInstitutionalReditCode(StringUtils.isBlank(borrowOrg.getUnifiedSocialCreditCode()) ? borrowOrg.getCharterCode() : borrowOrg.getUnifiedSocialCreditCode()));
			responseDTO.setBorrowerAddress(borrowOrg.getAddress());
			if (StringUtils.isNotBlank(project.getBorrowEmail()) && !project.getBorrowEmail().equals("\" \"")) {
				responseDTO.setBorrowEmail(project.getBorrowEmail());
			} else {
				responseDTO.setBorrowEmail(borrowOrg.getEmail());
			}
		} else {
			UserIdentityInfoResponseDTO borrower = userIdentityDubboService.get(project.getBorrowUserId());
			responseDTO.setBorrowerIdCard(privacyMasks.maskIdCardSixTwo(borrower.getIdCard()));
			if (StringUtils.isNotBlank(project.getBorrowEmail()) && !project.getBorrowEmail().equals("\" \"")) {
				responseDTO.setBorrowEmail(project.getBorrowEmail());
			} else {
				UserPersonalDubboInfoDTO userPersonalDubboInfoDTO = userDataDubboService.userPersonalInfo(project.getBorrowUserId());
				responseDTO.setBorrowEmail(userPersonalDubboInfoDTO.getEmail());
			}
			responseDTO.setBorrowerAddress(borrower.getAddress());
		}

		UserIdentityInfoResponseDTO transfer = userIdentityDubboService.get(creditorSaleDetail.getUserId());
		responseDTO.setTransferName(creditorSaleDetail.getRealName());
		responseDTO.setTransferNickName(creditorSaleDetail.getNickName());
		responseDTO.setTransferIdCard(privacyMasks.maskIdCardSixTwo(transfer.getIdCard()));
		if (!transferUserId.equals(userId)) {
			UserIdentityInfoResponseDTO assignee = userIdentityDubboService.get(userId);
			UserBasicInfoResponseDTO userBasicInfo = userBasicInfoDubboService.getUserBasicInfo(userId);
			responseDTO.setAssigneeName(assignee.getName());
			responseDTO.setAssigneeNickName(userBasicInfo.getNickName());
			responseDTO.setAssigneeIdCard(privacyMasks.maskIdCardSixTwo(assignee.getIdCard()));
		}

		responseDTO.setProjectTitle(project.getTitle().replaceAll("借款用途：", "").replaceAll("借款用于", ""));
		responseDTO.setContractNo("b_" + project.getProjectId());
		responseDTO.setRepaymentType(RepaymentTypeEnum.enumOf(project.getRepaymentType()).getDesc());
		responseDTO.setReceivableAmount(creditorSaleDetail.getShare());
		responseDTO.setCapitalReceivableAmount(SysNumberUtil.amountChineseFormat(responseDTO.getReceivableAmount().toPlainString()));
		// 债权所产生利息 = 源债权价值([出让金额 + 降价金额] == [债权本金 + 占用本金期限所产生利息]) - 剩余本金
		responseDTO.setReceivableInterest(creditorSaleDetail.getSaleAmount().add(creditorSaleDetail.getOffAmount()).subtract(creditorSaleDetail.getShare()));
		responseDTO.setCapitalReceivableInterest(SysNumberUtil.amountChineseFormat(responseDTO.getReceivableInterest().toPlainString()));
		responseDTO.setTransferredEffectiveDate(FastDateFormat.getInstance("yyyy年MM月dd日").format(new Date()));
		// 转让价格=债权出让价格+出让期间利息
		responseDTO
				.setTransferAmount(creditorSaleDetail.getSaleAmount().add(creditorSaleDetail.getSellingInterest()).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));
		responseDTO.setCapitalTransferAmount(SysNumberUtil.amountChineseFormat(responseDTO.getTransferAmount().toPlainString()));
		String deadlineUnit = PeriodUnitEnum.MONTH.getValue().equals(project.getDeadlineUnit()) ? "个月" : "天";
		responseDTO.setOriginalPeriods(project.getDeadline() + deadlineUnit);

		if (creditorSaleDetail.getRemainingDeadlineMonth() > 0 && creditorSaleDetail.getRemainingDeadlineDay() > 0) {
			responseDTO.setRemainingPeriods(creditorSaleDetail.getRemainingDeadlineMonth() + "个月" + creditorSaleDetail.getRemainingDeadlineDay() + "天");
		} else if (creditorSaleDetail.getRemainingDeadlineMonth() > 0 && creditorSaleDetail.getRemainingDeadlineDay() == 0) {
			responseDTO.setRemainingPeriods(creditorSaleDetail.getRemainingDeadlineMonth() + "个月");
		} else if (creditorSaleDetail.getRemainingDeadlineMonth() == 0 && creditorSaleDetail.getRemainingDeadlineDay() > 0) {
			responseDTO.setRemainingPeriods(creditorSaleDetail.getRemainingDeadlineDay() + "天");
		}
		responseDTO.setFinalDate(FastDateFormat.getInstance("yyyy年MM月dd日").format(creditorSaleDetail.getEndDate()));
		responseDTO.setBorrowRate(project.getBorrowRate().multiply(new BigDecimal(AmountHandleConstant.HUNDRED)).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));
		responseDTO.setServiceFeeRate(
				creditorSaleDetail.getExpenseRate().multiply(new BigDecimal(AmountHandleConstant.HUNDRED)).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));

		Map<String, String> tradeConfigMap = tradeConfigLocalService.selectTradeConfigByEmuns(TradeConfigEnum.DEBT_MAX_TRANSFER_COUNT, // 转让中债权最大数量
				TradeConfigEnum.DEBT_MIN_TRANSFER_AMOUNT, // 债权最低可转让金额
				TradeConfigEnum.DEBT_MIN_HAND_PERIOD, // 手持债权最少期数
				TradeConfigEnum.DEBT_MIN_REMAIN_PERIOD, // 最少剩余回款期数
				TradeConfigEnum.DEBT_LIMIT_TRANSFER_DAYS, // 限制债权转让时间
				TradeConfigEnum.DEBT_APPLY_TIME_BEGIN, // 申请债权转让时间开始
				TradeConfigEnum.DEBT_APPLY_TIME_END, // 申请债权转让时间结束
				TradeConfigEnum.DEBT_TRANSFER_TIME_LIMIT); // 债权转让购买时效

		log.info("creditorTransferProtocolPreview tradeConfigMap={}", JSON.toJSONString(tradeConfigMap));
		String transferredNumber = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MAX_TRANSFER_COUNT.getConfigKey(), "10");
		String minimumTransferAmount = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_TRANSFER_AMOUNT.getConfigKey(), "100");
		String minimumHoldPeriods = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_HAND_PERIOD.getConfigKey(), "3");
		String minimumRemainPeriods = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_REMAIN_PERIOD.getConfigKey(), "1");
		String transferredLimitDay = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_LIMIT_TRANSFER_DAYS.getConfigKey(), "1");
		String specifiedStartTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_APPLY_TIME_BEGIN.getConfigKey(), "8:00");
		String specifiedEndTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_APPLY_TIME_END.getConfigKey(), "22:00");
		String transferLimitTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_TRANSFER_TIME_LIMIT.getConfigKey(), "72");

		responseDTO.setTransferredNumber(Integer.valueOf(transferredNumber));
		responseDTO.setMinimumTransferAmount(minimumTransferAmount);
		responseDTO.setMinimumHoldPeriods(Integer.valueOf(minimumHoldPeriods));
		responseDTO.setMinimumRemainPeriods(Integer.valueOf(minimumRemainPeriods));
		responseDTO.setTransferredLimitDay(Integer.valueOf(transferredLimitDay));
		responseDTO.setSpecifiedStartTime(specifiedStartTime);
		responseDTO.setSpecifiedEndTime(specifiedEndTime);
		responseDTO.setTransferLimitTime(transferLimitTime);
		responseDTO.setSignDate(FastDateFormat.getInstance("yyyy年MM月dd日").format(new Date()));
		log.info("creditorTransferProtocolPreview creditorTransferProtocol={}", JSON.toJSONString(responseDTO));
		return responseDTO;
	}

	/**
	 * 债权转让协议-出让人预览
	 *
	 * @param investOrderId
	 *            出让订单ID
	 * @param saleAmount
	 *            出让金额
	 * @return TransferProtocolPreviewResponseDTO
	 */
	public SaleCreditorTransferProtocolPreviewResponseDTO saleCreditorTransferProtocolPreview(Long investOrderId, BigDecimal saleAmount) {
		log.info("transferProtocolPreview investOrderId={}, saleAmount={}", investOrderId, saleAmount);

		SaleCreditorTransferProtocolPreviewResponseDTO responseDTO = new SaleCreditorTransferProtocolPreviewResponseDTO();
		InvestorCreditor investorCreditor = investorCreditorMapperExt.selectByInvestOrderId(investOrderId);
		Project project = projectMapperExt.selectByPrimaryKey(investorCreditor.getProjectId());

		responseDTO.setProtocolNo("ZQ" + FastDateFormat.getInstance("yyMMdd").format(new Date()));
		responseDTO.setNiiwooReditCode(privacyMasks.maskInstitutionalReditCode(PlateformConstant.NIIWOO_REDIT_CODE));
		responseDTO.setNiiwooAddress("广东省深圳市南山区科技园北区乌石头路8号天明科技大厦主楼11楼、12楼1202室");
		responseDTO.setBorrowerName(project.getBorrowUserName());
		responseDTO.setBorrowerNickName(project.getBorrowNickName());

		if (BorrowerTypeEnum.COMPANY.getType().equals(project.getBorrowerType())) {
			SimpleOrgInfo borrowOrg = organizationDubboService.getByOrgId(project.getBorrowUserId());
			responseDTO.setBorrowerAddress(borrowOrg.getAddress());
			responseDTO.setBorrowerIdCard(
					privacyMasks.maskInstitutionalReditCode(StringUtils.isBlank(borrowOrg.getUnifiedSocialCreditCode()) ? borrowOrg.getCharterCode() : borrowOrg.getUnifiedSocialCreditCode()));
			if (StringUtils.isNotBlank(project.getBorrowEmail()) && !project.getBorrowEmail().equals("\" \"")) {
				responseDTO.setBorrowEmail(project.getBorrowEmail());
			} else {
				responseDTO.setBorrowEmail(borrowOrg.getEmail());
			}
		} else {
			UserIdentityInfoResponseDTO borrower = userIdentityDubboService.get(project.getBorrowUserId());
			responseDTO.setBorrowerIdCard(privacyMasks.maskIdCardSixTwo(borrower.getIdCard()));
			if (StringUtils.isNotBlank(project.getBorrowEmail()) && !project.getBorrowEmail().equals("\" \"")) {
				responseDTO.setBorrowEmail(project.getBorrowEmail());
			} else {
				UserPersonalDubboInfoDTO userPersonalDubboInfoDTO = userDataDubboService.userPersonalInfo(project.getBorrowUserId());
				responseDTO.setBorrowEmail(userPersonalDubboInfoDTO.getEmail());
			}
			responseDTO.setBorrowerAddress(borrower.getAddress());
		}

		UserIdentityInfoResponseDTO transfer = userIdentityDubboService.get(investorCreditor.getInvestUserId());
		UserBasicInfoResponseDTO userBasicInfo = userBasicInfoDubboService.getUserBasicInfo(investorCreditor.getInvestUserId());
		responseDTO.setTransferName(transfer.getName());
		responseDTO.setTransferNickName(userBasicInfo.getNickName());
		responseDTO.setTransferIdCard(privacyMasks.maskIdCardSixTwo(transfer.getIdCard()));

		responseDTO.setProjectTitle(project.getTitle().replaceAll("借款用途：", "").replaceAll("借款用于", ""));
		responseDTO.setContractNo("b_" + project.getProjectId());
		responseDTO.setRepaymentType(RepaymentTypeEnum.enumOf(project.getRepaymentType()).getDesc());
		responseDTO.setReceivableAmount(investorCreditor.getCapital().subtract(investorCreditor.getReceivedCapital()));
		responseDTO.setCapitalReceivableAmount(SysNumberUtil.amountChineseFormat(responseDTO.getReceivableAmount().toPlainString()));
		BigDecimal receivableInterest = investorCreditor.getCurrentInterest()
				.divide(new BigDecimal(RepaymentUtil.getOverdueDay(investorCreditor.getLastPeriodReceivedDate(), investorCreditor.getReceivedDate())), 6, BigDecimal.ROUND_HALF_EVEN)
				.multiply(new BigDecimal(RepaymentUtil.getOverdueDay(investorCreditor.getLastPeriodReceivedDate(), new Date()) - 1));
		responseDTO.setReceivableInterest(receivableInterest.setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));
		responseDTO.setCapitalReceivableInterest(SysNumberUtil.amountChineseFormat(responseDTO.getReceivableInterest().toPlainString()));
		responseDTO.setTransferAmount(saleAmount);
		responseDTO.setCapitalTransferAmount(SysNumberUtil.amountChineseFormat(responseDTO.getTransferAmount().toPlainString()));
		String deadlineUnit = PeriodUnitEnum.MONTH.getValue().equals(project.getDeadlineUnit()) ? "个月" : "天";
		responseDTO.setOriginalPeriods(project.getDeadline() + deadlineUnit);
		String RemainingPeriods = investorCreditor.getTotalPeriod() - investorCreditor.getCurrentPeriod() + "个月";
		Integer day = RepaymentUtil.getOverdueDay(new Date(), investorCreditor.getReceivedDate()) + 1;
		responseDTO.setRemainingPeriods(day > 0 ? RemainingPeriods + day + "天" : RemainingPeriods);
		responseDTO.setFinalDate(FastDateFormat.getInstance("yyyy年MM月dd日").format(investorCreditor.getExpectOverDate()));
		responseDTO.setBorrowRate(project.getBorrowRate().multiply(new BigDecimal(AmountHandleConstant.HUNDRED)).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));

		// 服务费
		List<CreditorRateConfig> creditorRateConfigList = creditorRateConfigMapperExt.getAllCreditorRateConfigService();
		for (CreditorRateConfig creditorRateConfig : creditorRateConfigList) {
			if (investorCreditor.getRepaymentType().equals(creditorRateConfig.getRepaymentType()) && investorCreditor.getReceivedPeriod() >= creditorRateConfig.getMinCollectedPeriods()
					&& investorCreditor.getReceivedPeriod() <= creditorRateConfig.getMaxCollectedPeriods()) {
				responseDTO.setServiceFeeRate(
						creditorRateConfig.getTransferRate().multiply(new BigDecimal(AmountHandleConstant.HUNDRED)).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO, BigDecimal.ROUND_HALF_EVEN));
			}
		}

		Map<String, String> tradeConfigMap = tradeConfigLocalService.selectTradeConfigByEmuns(TradeConfigEnum.DEBT_MAX_TRANSFER_COUNT, // 转让中债权最大数量
				TradeConfigEnum.DEBT_MIN_TRANSFER_AMOUNT, // 债权最低可转让金额
				TradeConfigEnum.DEBT_MIN_HAND_PERIOD, // 手持债权最少期数
				TradeConfigEnum.DEBT_MIN_REMAIN_PERIOD, // 最少剩余回款期数
				TradeConfigEnum.DEBT_LIMIT_TRANSFER_DAYS, // 限制债权转让时间
				TradeConfigEnum.DEBT_APPLY_TIME_BEGIN, // 申请债权转让时间开始
				TradeConfigEnum.DEBT_APPLY_TIME_END, // 申请债权转让时间结束
				TradeConfigEnum.DEBT_TRANSFER_TIME_LIMIT); // 债权转让购买时效

		log.info("creditorTransferProtocolPreview tradeConfigMap={}", JSON.toJSONString(tradeConfigMap));
		String transferredNumber = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MAX_TRANSFER_COUNT.getConfigKey(), "10");
		String minimumTransferAmount = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_TRANSFER_AMOUNT.getConfigKey(), "100");
		String minimumHoldPeriods = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_HAND_PERIOD.getConfigKey(), "3");
		String minimumRemainPeriods = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_MIN_REMAIN_PERIOD.getConfigKey(), "1");
		String transferredLimitDay = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_LIMIT_TRANSFER_DAYS.getConfigKey(), "1");
		String specifiedStartTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_APPLY_TIME_BEGIN.getConfigKey(), "8:00");
		String specifiedEndTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_APPLY_TIME_END.getConfigKey(), "22:00");
		String transferLimitTime = MapUtils.getString(tradeConfigMap, TradeConfigEnum.DEBT_TRANSFER_TIME_LIMIT.getConfigKey(), "72");

		responseDTO.setTransferredNumber(Integer.valueOf(transferredNumber));
		responseDTO.setMinimumTransferAmount(minimumTransferAmount);
		responseDTO.setMinimumHoldPeriods(Integer.valueOf(minimumHoldPeriods));
		responseDTO.setMinimumRemainPeriods(Integer.valueOf(minimumRemainPeriods));
		responseDTO.setTransferredLimitDay(Integer.valueOf(transferredLimitDay));
		responseDTO.setSpecifiedStartTime(specifiedStartTime);
		responseDTO.setSpecifiedEndTime(specifiedEndTime);
		responseDTO.setTransferLimitTime(transferLimitTime);
		responseDTO.setSignDate(FastDateFormat.getInstance("yyyy年MM月dd日").format(new Date()));

		log.info("transferProtocolPreview transferProtocolPreviewResponseDTO={}", JSON.toJSONString(responseDTO));
		return responseDTO;
	}

	/**
	 * 获取债权投资结果
	 *
	 * @param investOrderId
	 * @return
	 */
	@Transactional
	public CreditSuccessInfoDTO getCreditSuccessInfo(Long investOrderId) {
		CreditSuccessInfoDTO result = new CreditSuccessInfoDTO();
		ProjectInvestOrder investOrder = projectInvestOrderMapperExt.selectByPrimaryKey(investOrderId);
		result.setStatus(investOrder.getStatus());
		result.setInvestAmount(investOrder.getAmount());
		result.setInterest(investOrder.getInterest());
		result.setUserId(investOrder.getUserId());
		result.setAmount(investOrder.getShare());
		CreditorSaleDetail creditorSaleDetail = creditorSaleDetailMapperExt.selectByPrimaryKeyAndSaleUserId(investOrder.getCreditorSaleDetailId(), investOrder.getCreditorSaleUserId());
		result.setOffAmount(creditorSaleDetail.getOffAmount());
		ProjectBorrowerSummary borrowerSummary = borrowerSummaryMapperExt.selectByPrimaryKey(investOrder.getProjectId());
		result.setCurrentDate(borrowerSummary.getRefundDate());
		return result;
	}

	/**
	 * 适配加息活动
	 *
	 * @return
	 */
	public InvestAddInterestRateConfig findInvestAddInterestRateConfig(List<InvestAddInterestRateConfig> configs, Date publishTime, String orgId, Byte deadlineUnit, Integer deadline,
																	   Byte repaymentType) {
		if (!CollectionUtils.isEmpty(configs)) {
			for (InvestAddInterestRateConfig config : configs) {
				if (config.getEndTime().getTime() >= publishTime.getTime() && config.getStartTime().getTime() <= publishTime.getTime() && config.getOrgId().equals(orgId)
						&& config.getDeadlineUnit().equals(deadlineUnit) && config.getDeadline().equals(deadline) && config.getRepaymentType().equals(repaymentType)) {
					return config;
				}
			}
		}
		return null;
	}

	/**
	 * 新手标 调用
	 *
	 * @param userId
	 * @param channelCode
	 * @return
	 */
	public NoviceInvestInfoResponseDTO getNoviceInvestInfos(String userId, String channelCode) {
		NoviceInvestInfoResponseDTO responseDTO = new NoviceInvestInfoResponseDTO();
		NewbieProjectConfig config = newbieProjectConfigMapper.selectNewbieProjectConfig(channelCode);
		if (config == null) {
			// 不是新手标
			return responseDTO;
		}
		responseDTO.setIsNewProject((byte) 1);
		responseDTO.setNoviceInvestBannerUrl(config.getBanner());
		if (StringUtils.isBlank(userId)) {
			return responseDTO;
		}
		NoviceIntelligenceInvestInfoResponseDTO dto = noviceIntelligenceInvestService.getNoviceBasicInfo(userId);
		responseDTO.setIsNovice(dto.getDays() > 0 ? (byte) 1 : (byte) 0);
		responseDTO.setInvestmentNorm(dto.getInvestmentNorm());
		responseDTO.setRoleDefinitionDays(dto.getRoleDefinitionDays());
		responseDTO.setNoviceSurplusInvestMoney(dto.getNoviceSurplusInvestMoney());
		responseDTO.setNoviceSurplusDays(dto.getNoviceSurplusDays());
		return responseDTO;
	}

	public void completeLoanTimes(List<InvestingProjectDTO> list) {
		Set<String> borrowUserIdSet = new HashSet<>();
		for (InvestingProjectDTO investingProjectDTO : list) {
			borrowUserIdSet.add(investingProjectDTO.getBorrowerUserId());
		}

		if (!borrowUserIdSet.isEmpty()) {
			List<UserSuccessProjectCountDTO> userSuccessProjectCountDTOS = projectMapperExt
					.selectUserSuccessProjectCountDTOList(Arrays.asList(borrowUserIdSet.toArray(new String[borrowUserIdSet.size()])));
			Map<String, Integer> userIdSuccessCountMap = new HashMap<>();
			for (UserSuccessProjectCountDTO userSuccessProjectCountDTO : userSuccessProjectCountDTOS) {
				userIdSuccessCountMap.put(userSuccessProjectCountDTO.getUserId(), userSuccessProjectCountDTO.getSuccessCount());
			}
			for (InvestingProjectDTO investingProjectDTO : list) {
				Integer successCount = userIdSuccessCountMap.get(investingProjectDTO.getBorrowerUserId());
				if (null != successCount) {
					if (successCount >= 1) {
						investingProjectDTO.setLoanTimes(successCount - 1);
					} else {
						investingProjectDTO.setLoanTimes(0);
					}
				}
			}
		}
	}

	/**
	 * 是否可建群
	 *
	 * @param projectType
	 * @param investType
	 * @param investorType
	 * @param orgId
	 * @return
	 */
	private boolean enableAddGroup(Byte projectType, Byte investType, Byte investorType, String orgId){
		// 普通投资
		if(Byte.valueOf("1").equals(investType)
				&& Byte.valueOf("1").equals(investorType)){ // 个人投资人
			if(!ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectType)// 非机构标或
					|| (ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectType)) && OrgConstant.NIWOTA_ORG_ID.equals(orgId)){ // 机构标中的你我他机构(暖心借标的)
				return true;
			}
		}
		return false;
	}


	public InvestmentRateInfoResponseDTO getInvestmentRateInfo(String userId, Byte userIdentity) {
		InvestmentRateInfoResponseDTO dto = new InvestmentRateInfoResponseDTO();
		InvestmentRateInfoResponseDTO.DeadlineAndRateInfo intelligence = new InvestmentRateInfoResponseDTO.DeadlineAndRateInfo();
		InvestmentRateInfoResponseDTO.DeadlineAndRateInfo invest = new InvestmentRateInfoResponseDTO.DeadlineAndRateInfo();
		InvestmentRateInfoResponseDTO.DeadlineAndRateInfo creditor = new InvestmentRateInfoResponseDTO.DeadlineAndRateInfo();

		//散标投资利率
		HashMap<String, BigDecimal> investMap = projectInvestingMapperExt.selectProjectInvestingListByRate();
		if (investMap != null && investMap.get("max") != null && investMap.get("max").compareTo(investMap.get("min")) > 0) {
			invest.setRateBegin(investMap.get("min"));
			invest.setRateEnd(investMap.get("max"));
		} else {
			invest.setRateEnd(new BigDecimal("0.12"));
			invest.setRateBegin(new BigDecimal("0.085"));

		}
		//散标投资期限
		List<HashMap<String, Object>> projectInvestingDeadLine = projectInvestingMapperExt.selectProjectInvestingListByDeadLine();
		if (projectInvestingDeadLine != null && projectInvestingDeadLine.size() > 0) {
			getDeadLineInfo(invest, projectInvestingDeadLine);
		}
		invest.setType((byte)2);
		ProjectStartInvestAndIncreResponseDTO responseDTO = myLoanLocalService.getProjectStartInvestAndIncrement();
		invest.setStartInvestAmount(responseDTO.getStartInvestMoney());

		IntelligenceInvestSwitchResponseDTO creditorSwitch = intelligenceInvestService.intelligenceInvestSwitch(IntelligenceSwitchSourceEnum.NORMAL.getCode());
		if(creditorSwitch.getIntelligenceInvestSwitch() == (byte)1){

			IntelligenceInvestPageRequestDTO requestDTO = new IntelligenceInvestPageRequestDTO();
			requestDTO.setUserId(userId);
			requestDTO.setUserIdentity(userIdentity);
			requestDTO.setPackageChannelCode("app");
			//包投资利率
			HashMap<String, BigDecimal> packageMap = projectPackageMapperExt.selectPackageListByRate(requestDTO);
			if (packageMap != null && packageMap.get("max") != null && packageMap.get("max").compareTo(packageMap.get("min")) > 0) {
				intelligence.setRateBegin(packageMap.get("min"));
				intelligence.setRateEnd(packageMap.get("max"));
			} else {
				intelligence.setRateBegin(new BigDecimal("0.085"));
				intelligence.setRateEnd(new BigDecimal("0.12"));
			}
			//包投资期限
			List<HashMap<String, Object>> packageDeadLine = projectPackageMapperExt.selectProjectInvestingListByDeadLine(requestDTO);
			if(packageDeadLine != null){
				if (packageDeadLine.size() > 0) {
					getDeadLineInfo(intelligence, packageDeadLine);
				}
			}
			//查询最小的起投金额
			HashMap<String, BigDecimal> packageStartInvest = projectPackageMapperExt.selectPackageByStartInvestAmount(requestDTO);
			intelligence.setStartInvestAmount(new BigDecimal("1000"));
			if (packageStartInvest != null) {
				if (packageStartInvest.get("min") != null) {
					intelligence.setStartInvestAmount(packageStartInvest.get("min"));
				}
			}
			intelligence.setType((byte)1);
			dto.setIntelligenceInvest(intelligence);
		}
		//债转
		CreditorConfigDTO configDTO = creditorConfigDubboService.getCreditorConfigs();
		if (configDTO != null) {
			if(configDTO.getCreditorSwitch() == 1){
				creditor.setRateBegin(new BigDecimal("0.10"));
				creditor.setRateEnd(new BigDecimal("0.15"));
				creditor.setDeadlineBegin(1);
				creditor.setDeadlineEnd(9);
				creditor.setDeadlineUnitBegin(DeadlineUnitEnum.BY_MONTH.getDeadlineUnit());
				creditor.setDeadlineUnitEnd(DeadlineUnitEnum.BY_MONTH.getDeadlineUnit());
				creditor.setType((byte)3);
				dto.setDebtTransfer(creditor);
			}
		}
		dto.setInvest(invest);
		return dto;
	}

	private void getDeadLineInfo(InvestmentRateInfoResponseDTO.DeadlineAndRateInfo invest, List<HashMap<String, Object>> projectInvestingDeadLine) {
		if (projectInvestingDeadLine.size() == 1) {
			Integer max = (Integer) projectInvestingDeadLine.get(0).get("max");
			Integer min = (Integer) projectInvestingDeadLine.get(0).get("min");
			Integer deadline = (Integer) projectInvestingDeadLine.get(0).get("unit");
            //月标
			if(max != min){
				invest.setDeadlineBegin(min);
				invest.setDeadlineEnd(max);
				invest.setDeadlineUnitBegin(Byte.parseByte(deadline.toString()));
				invest.setDeadlineUnitEnd(Byte.parseByte(deadline.toString()));
			}
        }
	}
}
